recruiting-ops-mcp
Provides tools for searching repositories, listing repository languages, and searching commits, enabling research into engineering candidates' public work.
Provides tools for listing threads, retrieving message attachments, sending emails, and creating drafts, used for application intake and candidate communication.
Provides tools for listing and inserting calendar events, used to schedule interview panels and new-hire meetings.
Integrates with Google Docs to create and fill offer letters, job descriptions, and survey summary documents.
Provides tools for creating files, copying files, and managing permissions, used to store CVs, offer letters, and onboarding folders.
Integrates with Google Forms to send pulse surveys and collect responses for leadership summaries.
Provides tools for appending and reading spreadsheet values, used to track candidates and interview schedules.
Integrates with Linear to manage team memberships and onboarding tasks for new hires.
Provides tools for creating, updating, and retrieving pages, used for candidate pages, interview debriefs, onboarding docs, and offer templates.
Provides tools for posting messages and inviting users to channels, used for hiring updates, onboarding, and pulse surveys.
Click on "Deploy Server".
Wait a few minutes for the server to deploy. Once ready, it will show a "Started" state.
In the chat, type
@followed by the MCP server name and your instructions, e.g., "@recruiting-ops-mcp file new applications from my Gmail into the candidate sheet"
That's it! The server will respond to your query, and you can continue using it as needed.
Here is a step-by-step guide with screenshots.
Recruiting and Onboarding
Applications to a candidate sheet, interview scheduling, debriefs and a new hire's day one.
An MCP server with 8 workflows across Gmail, Google Drive, Google Sheets, Google Calendar, Granola, Notion, Slack, GitHub, Linear, Google Forms, Google Docs, Tavily and Firecrawl. Each workflow is a prompt your agent runs as a slash command, over the 27 tools it needs and no others.
uv tool install https://github.com/r28ai/recruiting-ops-mcp/releases/download/v0.1.0/recruiting_ops_mcp-0.1.0-py3-none-any.whl
claude mcp add hiring -- recruiting-ops-mcpIt installs with uv from this repository's release, with no git and nothing to build; nothing but Charter and the libraries it uses comes from PyPI. To update, run the install line from the latest release. If a desktop app cannot find recruiting-ops-mcp, give it the full path from which recruiting-ops-mcp (where recruiting-ops-mcp on Windows).
Then ask your agent to connect your apps, or run /mcp__hiring__setup.
Connect your apps
Ask the agent to connect one ("connect Linear"). It tells you where to get that app's key and the command that stores it, and the next call works, with no restart. The agent never asks for a key in the chat.
Or connect everything this server uses from a terminal:
recruiting-ops-mcp login # each app in turn
recruiting-ops-mcp login granola # just one
recruiting-ops-mcp status # what is connectedTokens and keys go to your operating system's keychain (macOS Keychain, Windows Credential Manager, the Secret Service on Linux), and are checked with one read-only call to the app's own API before they are kept. Every key, token and OAuth client is yours: we register no app with any of these services, and nothing passes through a server of ours, because there isn't one.
App | How it connects | Or set |
Browser sign-in, over your own OAuth client (make one). |
| |
Granola | Your own key (get one), entered once. In the Granola app: Settings → Connectors → API keys. Business plan or above. |
|
Notion | Your own key (get one), entered once. Then share the pages it should see with the integration. |
|
Slack | Your own key (get one), entered once. A bot token from your own Slack app, which the guide sets up in about three minutes. |
|
GitHub | Your own key (get one), entered once. |
|
Linear | Your own key (get one), entered once. |
|
Tavily | Your own key (get one), entered once. |
|
Firecrawl | Your own key (get one), entered once. |
|
A variable set in your client's config always wins over the keychain.
Related MCP server: Lever MCP Server
Workflows
Workflow | What you get | Apps |
Applications inbox → candidate sheet | Every application filed with its CV and a row, no ATS needed at ten hires a year. | Gmail, Google Drive, Google Sheets |
Interview scheduling | Panels booked around everyone's calendars with the candidate confirmed. | Google Sheets, Google Calendar, Gmail |
Interview debrief | Each interviewer's notes summarised onto the candidate page, posted to the hiring channel. | Granola, Notion, Slack |
Engineering candidate's public work | What they've built in public, summarised before the technical interview. | GitHub, Notion |
New hire day one | Week-one meetings, channels, team, folders and checklist, from one name and start date. | Google Calendar, Slack, Linear, Google Drive, Notion |
Pulse survey | Survey sent where people are, results summarised for leadership. | Google Forms, Slack, Google Docs |
Offer letter | Template copied, filled from the candidate page and drafted to send. | Notion, Google Drive, Google Docs, Gmail |
Job post from the market | A job description benchmarked against how others describe and pay the role. | Tavily, Firecrawl, Google Docs |
Every prompt takes one optional argument, details: the repo, team, channel, customer or date range you mean, so the agent does not have to ask. In Claude Code, put it in quotes, or only its first word arrives:
/mcp__hiring__applications_inbox_to_candidate_sheet "the jobs@ inbox, sheet 'Candidates 2026'"Reads run without asking. Before anything that creates, sends, changes or deletes, the prompt tells the agent to show you the call and wait.
1 of the 8 workflows need no Google or Granola credential.
Other clients
Claude Desktop: install uv if you have not, since Claude Desktop starts the server with it, then open the .mcpb from the latest release. Claude asks for any keys in its own settings and keeps them in your keychain. The first start takes a few seconds longer, while uv installs it.
VS Code (.vscode/mcp.json): VS Code asks for each key the first time the server starts and stores it securely. Leave out any you stored with login.
{
"inputs": [
{
"type": "promptString",
"id": "google-client-secret",
"description": "Google: OAuth client secret",
"password": true
},
{
"type": "promptString",
"id": "granola-api-key",
"description": "Granola: API key",
"password": true
},
{
"type": "promptString",
"id": "notion-api-key",
"description": "Notion: Integration secret (ntn_\u2026)",
"password": true
},
{
"type": "promptString",
"id": "slack-bot-token",
"description": "Slack: Bot token (xoxb-\u2026)",
"password": true
},
{
"type": "promptString",
"id": "github-token",
"description": "GitHub: Personal access token",
"password": true
},
{
"type": "promptString",
"id": "linear-api-key",
"description": "Linear: Personal API key",
"password": true
},
{
"type": "promptString",
"id": "tavily-api-key",
"description": "Tavily: API key",
"password": true
},
{
"type": "promptString",
"id": "firecrawl-api-key",
"description": "Firecrawl: API key",
"password": true
}
],
"servers": {
"hiring": {
"type": "stdio",
"command": "recruiting-ops-mcp",
"env": {
"GOOGLE_CLIENT_SECRET": "${input:google-client-secret}",
"GRANOLA_API_KEY": "${input:granola-api-key}",
"NOTION_API_KEY": "${input:notion-api-key}",
"SLACK_BOT_TOKEN": "${input:slack-bot-token}",
"GITHUB_TOKEN": "${input:github-token}",
"LINEAR_API_KEY": "${input:linear-api-key}",
"TAVILY_API_KEY": "${input:tavily-api-key}",
"FIRECRAWL_API_KEY": "${input:firecrawl-api-key}",
"GOOGLE_CLIENT_ID": ""
}
}
}
}Cursor (.cursor/mcp.json) starts it the same way:
{
"mcpServers": {
"hiring": {
"command": "recruiting-ops-mcp"
}
}
}Codex (~/.codex/config.toml) starts a turn without waiting for a server unless it is required, and then the agent has none of its tools. required = true makes the session wait for it, and startup_readiness = "catalog" waits for its tool list rather than just its connection:
[mcp_servers.hiring]
command = "recruiting-ops-mcp"
required = true
startup_readiness = "catalog"
startup_timeout_sec = 30Name the server hiring. A host builds each tool's name from that key, and a longer one can push a tool past the 64 characters a function name allows.
Built with Charter
Every tool here is a Charter declaration: a Pydantic schema saying where each field goes on the wire. Charter's runtime builds the request, attaches and refreshes the credential, and trims the response before the model reads it. It runs in your process, with no proxy and no telemetry.
The 27 tool schemas come to 46,924 tokens.
The same tools work in your own agent, without MCP:
from charter.adapters.openai import to_openai_tools
from charter_packs_mcp import FAMILIES
tools = FAMILIES["people"].tools()
definitions = to_openai_tools(tools) # or charter.adapters.langchainNeed an API that isn't here? Write a pack: your coding agent writes the declarations, and Charter's conformance suite checks them.
Gmail:
gmail_threads_list,gmail_messages_attachments_get,gmail_messages_send,gmail_drafts_createGoogle Drive:
gdrive_files_create,gdrive_permissions_create,gdrive_files_copyGoogle Sheets:
gsheets_spreadsheets_values_append,gsheets_spreadsheets_values_getGoogle Calendar:
gcalendar_events_list,gcalendar_events_insertGranola:
granola_notes_getNotion:
notion_pages_update,notion_pages_create,notion_pages_retrieveSlack:
slack_chat_post_message,slack_conversations_inviteGitHub:
github_search_repositories,github_repos_list_languages,github_search_commitsLinear:
linear_team_membership_createGoogle Forms:
gforms_forms_create,gforms_forms_responses_listGoogle Docs:
gdocs_documents_create,gdocs_documents_batch_updateTavily:
tavily_searchFirecrawl:
firecrawl_scrape
License
Apache 2.0.
Available Tools
29 toolsconnectA
Connect one app this server uses. For an app that issues keys, says where to get one and the terminal command that stores it. For Google, once the user's own OAuth client is set, starts the browser sign-in and returns at once: the user approves in the browser and the next call works. To see which apps are connected, call connection_status. Never ask the user for a key in the chat.
Inputs: app (required).
connection_statusARead-only
See which apps this server is connected to, and how to connect each one that is not. Changes nothing.
Inputs: no named parameters.
firecrawl_scrapeB
Scrape a single URL and optionally extract information. Use when the user wants to read or summarize a specific webpage. Supports markdown, HTML, screenshots, and structured JSON extraction.
Inputs: url (required), proxy, maxAge, minAge, +22 more.
gcalendar_events_insertC
Create a calendar event; returns details of the event.
Inputs: event (required), calendarId (required), sendUpdates, maxAttendees, +3 more.
gcalendar_events_listCRead-onlyIdempotent
List events matching a given search filter.
Inputs: calendarId (required), q, iCalUID, orderBy, +14 more.
gdocs_documents_batch_updateA
Apply a list of edits to a document. Each request sets exactly one kind of edit — insert_text, replace_all_text, update_text_style, insert_table and so on. Edits apply in order against a document that shifts as they do: when inserting at several indices, order the requests back-to-front so earlier insertions do not move the positions later ones refer to.
Inputs: body (required), documentId (required).
gdocs_documents_createA
Create a blank document with a title. Only the title is honoured — the document is created empty. To add content, call this and then documents_batch_update with the returned documentId.
Inputs: body.
gdrive_files_copyA
Create a copy of a file and apply any requested updates with patch semantics. Typically send a new name and optionally parents.
Inputs: file (required), fileId (required), ocrLanguage, copyComments, +5 more.
gdrive_files_createA
Create a file's metadata: a folder (mimeType application/vnd.google-apps.folder), a Google Doc / Sheet / Slide, or an empty blob. Media upload is not expressed here. parents takes at most one folder ID.
Inputs: file (required), ocrLanguage, includeLabels, supportsAllDrives, +4 more.
gdrive_permissions_createA
Create a permission for a file or shared drive. type is user, group, domain or anyone; role is owner, organizer, fileOrganizer, writer, commenter or reader. For user or group, send emailAddress. Concurrent permission writes on the same file are not supported.
Inputs: fileId (required), permission (required), emailMessage, supportsAllDrives, +4 more.
gforms_forms_createA
Create a new form from a title. Only the title and the document title are honoured: the form is created with no description, no items and default settings. To add questions, call this and then forms_batch_update with the returned formId. Pass unpublished=true to create a form that does not yet accept responses.
Inputs: body (required), unpublished.
gforms_forms_responses_listARead-onlyIdempotent
List a form's submitted responses, newest page first, up to 5000 per page. The only supported filter is on submission time: pass filter='timestamp >= 2026-01-01T00:00:00Z' to read what has arrived since a point in time. Answers come back keyed by questionId.
Inputs: formId (required), filter, pageSize, pageToken.
github_repos_list_languagesARead-onlyIdempotent
List a repository's languages with bytes of code each — the quickest way to find out what a repository is written in before reading any of it. Answers with an object keyed by language, so there is nothing to page.
Inputs: repo (required), owner (required).
github_search_commitsARead-onlyIdempotent
Search commits by message, author or date across GitHub — 'repo:owner/name fix flaky test'. The way to find where a change was introduced. Rate limited to 30 requests per minute.
Inputs: q (required), page, sort, order, +1 more.
github_search_repositoriesARead-onlyIdempotent
Search repositories with qualifiers, e.g. 'topic:cli language:go stars:>500'. Rate limited to 30 requests per minute.
Inputs: q (required), page, sort, order, +1 more.
gmail_drafts_createC
Save an email draft to Gmail.
Inputs: body (required), userId.
gmail_messages_attachments_getARead-onlyIdempotent
Read one attachment, by the attachmentId messages_get or threads_get lists. A text file comes back as text; a binary one is reported, not decoded.
Inputs: id (required), messageId (required), userId.
gmail_messages_sendC
Send an email via the Gmail API.
Inputs: body (required), userId.
gmail_threads_listCRead-onlyIdempotent
List Gmail threads.
Inputs: q, userId, labelIds, pageToken, +2 more.
granola_notes_getARead-onlyIdempotent
Read one meeting note: its AI summary, the people who attended, the calendar event it was taken against, and the folders it belongs to. Pass include='transcript' to get the transcript inline as well. A transcript too large to inline answers 413 TRANSCRIPT_TOO_LARGE. Read it with notes_transcript_get instead.
Inputs: noteId (required), include.
gsheets_spreadsheets_values_appendC
Appends values to a spreadsheet.
Inputs: range (required), valueRange (required), spreadsheetId (required), valueInputOption (required), +4 more.
gsheets_spreadsheets_values_getCRead-onlyIdempotent
Returns a range of values from a spreadsheet.
Inputs: range (required), spreadsheetId (required), majorDimension, valueRenderOption, +1 more.
linear_team_membership_createC
Add a member to a team.
Inputs: variables (required).
notion_pages_createA
Create a page — as a subpage of another page, or as a row of a database by giving its data_source_id as the parent. Content comes as a markdown string Notion parses into blocks, or from a template: one or the other, never both.
Inputs: body (required), filterProperties.
notion_pages_retrieveARead-onlyIdempotent
Get a page's properties. This does not return the page's content — the blocks are read with blocks_children_list, or all at once as Markdown with pages_retrieve_markdown. A property holding more than 25 entries comes back truncated; read it in full with pages_retrieve_property_item.
Inputs: pageId (required), filterProperties.
notion_pages_updateA
Update a page's property values, icon, cover, or trash state. Properties not named are left alone, and a value set to null is cleared. This cannot move a page — use pages_move.
Inputs: body (required), pageId (required), filterProperties.
slack_chat_post_messageA
Send a message to a Slack channel, private group, or DM. Provide text for a plain message; set thread_ts to reply inside an existing thread.
Inputs: channel (required), text, parse, blocks, +13 more.
slack_conversations_inviteA
Add people to a channel. With force set, Slack adds everyone it can and reports the rest alongside a success, so read the result.
Inputs: users (required), channel (required), force.
tavily_searchA
Execute a real-time web search optimized for AI agents. Use when sources are unknown or current web context is needed. Prefer search_depth advanced with chunks_per_source 3 for stronger evidence per source.
Inputs: query (required), topic, country, endDate, +21 more.
Tool Schema Changelog
Recent tool additions, removals, and schema changes observed during successful MCP inspections.
Loading tool schema changelog…
TDQS
Scored across 29 tools
Each tool has a distinct service prefix and action, e.g., gmail_threads_list vs gmail_messages_attachments_get, making purposes clear. No two tools appear to do the same thing; overlapping actions are differentiated by resource and service.
Most tools follow a service_entity_action snake_case pattern (e.g., gdrive_files_create, notion_pages_update). Minor deviations like connect, connection_status, tavily_search, and slack_chat_post_message break the pattern but remain readable.
29 tools across 11+ services is excessive for a focused recruiting ops server; many services have only one or two operations. The set feels like a broad integration grab bag rather than a well-scoped toolkit.
Core CRUD is missing for most services: Gmail lacks message reading, GDrive lacks list/get, GSheets lacks spreadsheet creation, and GitHub/Linear have only search or membership creation. References to non-existent tools (e.g., blocks_children_list) indicate an incomplete surface.
Maintenance
Related MCP Connectors
Tailor resumes, score ATS fit, find hiring leads, and send cold outreach, from your agent.
Aya, Talpy's AI recruiter, in your assistant. Read jobs, candidates and evidence-backed interview scores; with write access, create jobs and send candidates to Aya's interview by WhatsApp or web link, in English, Spanish, German, French and Portuguese. See your plan and upgrade link. Hiring decisions stay human.
Recruitment ATS + CRM for agencies: candidates, jobs, pipeline and outreach, with your permissions.
Find your next great hire. Review candidates and manage hiring through your AI assistant.
Related MCP Servers
- FlicenseNot gradedqualityCmaintenanceEnables AI agents to pull live job listings from major ATS platforms (Greenhouse, Lever, Ashby, Workable), Hacker News hiring threads, and detect hiring signals on company career pages.-
- FlicenseNot gradedqualityDmaintenanceEnables AI agents to manage Lever.co candidates and job requisitions, and send themed emails via Gmail API with OAuth 2.0.-
- FlicenseNot gradedqualityCmaintenanceEnables Claude to securely search, read, and update your ATS data — candidates, mandates, pipelines, tasks, and interviews — through natural language, with configurable write permissions and confirmation for actions like scheduling and emailing.-
- AlicenseBqualityBmaintenanceEnables freelancers and agencies to run eight back-office workflows across Stripe, Google Drive, Linear, Google Calendar, Gmail, GitHub, Google Docs, Granola, Google Sheets and Firecrawl, covering client onboarding, invoices from calendar and commits, status reports, scope-creep detection, site audits and overdue invoice chasers. Reads run freely, while anything that creates, sends, changes or deletes is shown for approval first, with credentials kept in your own OS keychain and no proxying through any third-party server.28Apache 2.0