Recruiting Screener CrewAI
What it does
An intake agent converts the job description into explicit must-have and nice-to-have criteria. A screener reads each résumé from the drive folder and scores it against those criteria with cited evidence, ignoring names and schools to reduce bias. A ranker builds a shortlist, records it for the hiring team, and drafts a specific outreach note for the top candidates. A clean sequential crew; a human makes the actual call.
The cast · 3 agents
Turn the job description into a concrete rubric: must-have skills and experience, nice-to-haves, and clear disqualifiers. Keep it specific enough to score against.
Rank the candidates by score, produce a shortlist with a one-line reason each, record it for the hiring team, and draft a warm, specific outreach note for the top candidates. Do not send; leave the notes as drafts.
Read each résumé in the folder and score it against the rubric, citing the evidence for every point. Judge on demonstrated skills and experience only — ignore names, schools, and photos. Flag anything you could not verify.
Flow
Interface
Depends on · 3 MCP servers
Multi-account MCP for Gmail, Calendar, Drive, Docs, and Sheets — 61 tools, tokenio.github.adelaidasofia/google-workspace-mcpdriveAVerified
io.github.modelcontextprotocol/filesystemnot in the registry yetfs# Generated by @socketcat/compiler for target: langgraph
# blueprint: com.socketcat/recruiting-screener v1.0.0 schema: socketcat.dev/blueprint/v0
# This code is yours. Edit it freely. The socketcat_runtime helper is optional and can be vendored.
from typing import TypedDict
from langgraph.graph import StateGraph, START, END
import socketcat_runtime as _rt
class State(TypedDict, total=False):
jd: object
shortlist: object
criteria: object
scores: object
def node_AgentInvoke_0(state):
return _rt.run_agent("intake", state, {"jd":"jd"}, "criteria")
def node_AgentInvoke_1(state):
return _rt.run_agent("screener", state, {"criteria":"criteria"}, "scores")
def node_AgentInvoke_2(state):
return _rt.run_agent("ranker", state, {"scores":"scores"}, "shortlist")
def build():
b = StateGraph(State)
b.add_node("AgentInvoke_0", node_AgentInvoke_0)
b.add_node("AgentInvoke_1", node_AgentInvoke_1)
b.add_node("AgentInvoke_2", node_AgentInvoke_2)
b.add_edge(START, "AgentInvoke_0")
b.add_edge("AgentInvoke_0", "AgentInvoke_1")
b.add_edge("AgentInvoke_1", "AgentInvoke_2")
b.add_edge("AgentInvoke_2", END)
return b.compile()
INPUTS = ["jd"]
OUTPUTS = ["shortlist"]
if __name__ == "__main__":
_rt.main(build, INPUTS, OUTPUTS)
▸blueprint.json (the portable format)
{
"id": "com.socketcat/recruiting-screener",
"flow": {
"type": "sequence",
"blocks": [
{
"in": {
"jd": "jd"
},
"out": "criteria",
"use": "intake",
"type": "agent"
},
{
"in": {
"criteria": "criteria"
},
"out": "scores",
"use": "screener",
"type": "agent"
},
{
"in": {
"scores": "scores"
},
"out": "shortlist",
"use": "ranker",
"type": "agent"
}
]
},
"tags": [
"hr",
"recruiting",
"sequential",
"screening"
],
"title": "Recruiting Screener",
"agents": {
"intake": {
"model": {
"hint": "reasoning"
},
"title": "Intake",
"instructions": "Turn the job description into a concrete rubric: must-have skills and experience, nice-to-haves, and clear disqualifiers. Keep it specific enough to score against."
},
"ranker": {
"model": {
"hint": "fast"
},
"title": "Ranker",
"tools": [
"drive.draft_email"
],
"instructions": "Rank the candidates by score, produce a shortlist with a one-line reason each, record it for the hiring team, and draft a warm, specific outreach note for the top candidates. Do not send; leave the notes as drafts."
},
"screener": {
"model": {
"hint": "reasoning"
},
"title": "Screener",
"tools": [
"drive.list_files",
"drive.read_doc"
],
"instructions": "Read each résumé in the folder and score it against the rubric, citing the evidence for every point. Judge on demonstrated skills and experience only — ignore names, schools, and photos. Flag anything you could not verify."
}
},
"estate": {
"scores": {
"type": "array",
"description": "The screener's per-candidate scores with evidence."
},
"criteria": {
"type": "object",
"description": "The intake agent's must-haves and nice-to-haves from the JD."
}
},
"$schema": "socketcat.dev/blueprint/v0",
"authors": [
{
"url": "https://socketcat.com",
"name": "SocketCat"
}
],
"license": "MIT",
"servers": [
{
"ref": "io.github.adelaidasofia/google-workspace-mcp",
"alias": "drive"
},
{
"ref": "io.github.modelcontextprotocol/filesystem",
"alias": "fs"
},
{
"ref": "app.linear/linear",
"alias": "tracker"
}
],
"summary": "Turns a job description into scored, evidence-backed candidate rankings and drafts outreach to the top few.",
"targets": [
"crewai",
"langgraph",
"*"
],
"version": "1.0.0",
"interface": {
"inputs": {
"jd": {
"type": "string",
"description": "The job description, and the drive folder of résumés to screen."
}
},
"outputs": {
"shortlist": {
"type": "string",
"description": "The ranked shortlist with reasons and the drafted outreach notes."
}
}
},
"extensions": {
"com.crewai": {
"process": "sequential"
}
},
"description": "An intake agent converts the job description into explicit must-have and nice-to-have criteria. A screener reads each résumé from the drive folder and scores it against those criteria with cited evidence, ignoring names and schools to reduce bias. A ranker builds a shortlist, records it for the hiring team, and drafts a specific outreach note for the top candidates. A clean sequential crew; a human makes the actual call."
}More in Productivity & HR
Browse all →Inbox Chief of Staff
Triages your inbox, then routes each thread to the right handler — draft a reply, propose meeting times, or file it — with your approval before anything sends.