MCP Integration Guide
Connect external tools and automate recruiting workflows via the Model Context Protocol gateway.
1. Gateway Overview
What is MCP?
The Model Context Protocol (MCP) is an open standard that lets AI models call server-side tools over a lightweight HTTP transport. Applyer exposes a Streamable HTTP endpoint that any MCP-compatible client can connect to - including LLM agent frameworks, custom scripts, and ATS platforms.
Base URL
https://mcp.applyer.io/sseAll tool calls are sent as JSON-RPC messages to this single endpoint over Streamable HTTP (server-sent events for responses).
Rate Limits
- Per-key hourly - each API key is limited to a fixed number of requests per rolling hour. Exceeding the limit returns HTTP 429.
- Per-tenant daily - the tenant as a whole has a daily request cap across all keys. This prevents runaway automation from consuming the entire budget.
2. Available Tools
search_candidates
Search for anonymized candidate profiles by skills, experience, and location. Returns matching candidates without identifying information.
Parameters
| Name | Type | Required | Description |
|---|---|---|---|
| skills | string[] | Yes | Skills to search for (e.g. ["React", "TypeScript"]) |
| min_experience | integer | No | Minimum number of positions held (proxy for seniority). Min 0. |
| location | string | No | Location filter - partial match (e.g. "Tel Aviv") |
| limit | integer | No | Max results to return (1-50, default 20) |
skills
Type: string[]
Required: Yes
Skills to search for (e.g. ["React", "TypeScript"])
min_experience
Type: integer
Required: No
Minimum number of positions held (proxy for seniority). Min 0.
location
Type: string
Required: No
Location filter - partial match (e.g. "Tel Aviv")
limit
Type: integer
Required: No
Max results to return (1-50, default 20)
Response shape
Returns a list of anonymized candidates, each containing:
anonymous_id- opaque identifier (use with unlock_candidate)skill_list- string[] of matched skillsexperience_range- seniority bracketeducation_level- highest education leveldesired_role- candidate's target rolematch_score- numeric relevance scorematch_explanation- human-readable explanation of the match
unlock_candidate
Unlock a candidate's full profile including contact information. Requires an active tenant subscription. Idempotent - re-unlocking the same candidate is free.
Parameters
| Name | Type | Required | Description |
|---|---|---|---|
| candidate_anonymous_id | string | Yes | The anonymous ID from search_candidates results |
candidate_anonymous_id
Type: string
Required: Yes
The anonymous ID from search_candidates results
Response shape
Returns the candidate's full profile:
name- full nameemail- contact emailphone- phone numberlinkedin_url- LinkedIn profile URLgithub_url- GitHub profile URLdesired_role- target roleskills- string[] of extracted skills
3. Authentication
Steps
- Generate an API key - Only tenant super users can create MCP API keys. Navigate to your tenant settings and select "API Keys" to generate a new key. Copy it immediately - it will not be shown again.
- Set the Authorization header - Every request must include the key in the HTTP header:
Authorization: Bearer YOUR_API_KEY- Rotate keys regularly - Create a new key, update your integration, then revoke the old key. This ensures zero downtime during rotation.
- Never expose keys in client-side code - API keys should only be used in server-side code or secure environments. Never embed them in browser JavaScript, mobile apps, or public repositories.
4a. Use Case - ATS Integration
Steps
- Search for candidates matching the open role - Call search_candidates with the skills and location from your job requisition.
- Review anonymous results - Evaluate match_score and match_explanation to shortlist candidates before unlocking.
- Unlock selected candidates - Call unlock_candidate for each shortlisted profile to retrieve contact details.
- Import into your ATS - Map the returned fields (name, email, phone, LinkedIn) to your ATS candidate record and create the entry.
Example - TypeScript
import { Client } from "@modelcontextprotocol/sdk/client/index.js";
import { StreamableHTTPClientTransport } from "@modelcontextprotocol/sdk/client/streamableHttp.js";
const transport = new StreamableHTTPClientTransport(
new URL("https://mcp.applyer.io/sse"),
{ headers: { Authorization: "Bearer YOUR_API_KEY" } }
);
const client = new Client({ name: "my-ats", version: "1.0.0" });
await client.connect(transport);
// Step 1: Search for candidates
const searchResult = await client.callTool({
name: "search_candidates",
arguments: {
skills: ["React", "TypeScript", "Node.js"],
location: "Tel Aviv",
min_experience: 3,
limit: 10,
},
});
console.log("Search results:", searchResult.content);
// Step 2: Unlock a top candidate
const unlockResult = await client.callTool({
name: "unlock_candidate",
arguments: {
candidate_anonymous_id: "ANONYMOUS_ID_FROM_SEARCH",
},
});
console.log("Unlocked profile:", unlockResult.content);Example - curl
# Search for React developers in Tel Aviv
curl -X POST https://mcp.applyer.io/sse \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"jsonrpc": "2.0",
"id": 1,
"method": "tools/call",
"params": {
"name": "search_candidates",
"arguments": {
"skills": ["React", "TypeScript"],
"location": "Tel Aviv",
"limit": 5
}
}
}'4b. Use Case - Bulk Candidate Search
Steps
- Define your skill sets - Create an array of skill-set queries, one per open role or team need.
- Run searches in sequence - Call search_candidates for each skill set. Use the limit parameter to control page size (max 50 per call).
- Deduplicate by anonymous ID - Candidates matching multiple skill sets will appear in multiple result sets. Use anonymous_id to merge duplicates and track which queries each candidate matched.
- Rank by match score - Sort the merged list by match_score (descending) to surface the strongest candidates first.
Example - TypeScript
const skillSets = [
{ skills: ["React", "TypeScript"], location: "Tel Aviv" },
{ skills: ["Python", "FastAPI"], location: "Haifa" },
{ skills: ["Java", "Spring Boot"] },
];
const allResults = new Map<string, { candidate: string; queries: number }>();
for (const query of skillSets) {
const result = await client.callTool({
name: "search_candidates",
arguments: { ...query, limit: 50 },
});
// Parse text content and extract anonymous IDs + scores
const text = result.content[0]?.text ?? "";
// ... parse candidates from text response ...
// Deduplicate and count query matches
for (const candidate of parsed) {
const existing = allResults.get(candidate.anonymous_id);
if (existing) {
existing.queries += 1;
} else {
allResults.set(candidate.anonymous_id, {
candidate: candidate.anonymous_id,
queries: 1,
});
}
}
}
// Sort by number of matching queries (cross-skill relevance)
const ranked = [...allResults.values()]
.sort((a, b) => b.queries - a.queries);
console.log("Top candidates:", ranked.slice(0, 10));4c. Use Case - Analytics Export
Steps
- Review audit logs - Every search_candidates and unlock_candidate call is logged with a timestamp, tenant ID, key ID, and result metadata. Tenant super users can access these logs from the admin panel.
- Track search impressions - Each search logs a query hash, the number of results returned, and the latency in milliseconds. Use this to measure which skill sets have the deepest candidate pools.
- Track unlocks - Each unlock logs the anonymous ID and tenant details. Track unlock volume over time to measure conversion from search to outreach.
- Export from the admin panel - Tenant super users can view and export analytics data (JSON or CSV) directly from the admin panel. Navigate to your tenant dashboard and select the MCP analytics section.
Accessing analytics
Analytics are available through the Applyer admin panel - not via a separate REST API. The admin panel displays:
- Total searches and unique query hashes per period
- Total unlocks and unique candidates unlocked
- Top searched skill combinations
- Per-key usage breakdown
- Latency percentiles for search calls
Use the export button in the admin panel to download data as JSON or CSV for ingestion into BI tools like Metabase, Looker, or spreadsheets.
Example - client-side tracking
You can also build your own analytics by tracking MCP tool call results in your integration code:
// Track search and unlock metrics in your own system
interface McpMetrics {
searches: number;
unlocks: number;
candidatesSeen: Set<string>;
}
const metrics: McpMetrics = {
searches: 0,
unlocks: 0,
candidatesSeen: new Set(),
};
// After each search call
const searchResult = await client.callTool({
name: "search_candidates",
arguments: { skills: ["React", "TypeScript"], limit: 20 },
});
metrics.searches += 1;
// After each unlock call
const unlockResult = await client.callTool({
name: "unlock_candidate",
arguments: { candidate_anonymous_id: "ANONYMOUS_ID" },
});
metrics.unlocks += 1;
metrics.candidatesSeen.add("ANONYMOUS_ID");
// Compute conversion rate
const conversionRate = metrics.searches > 0
? ((metrics.unlocks / metrics.searches) * 100).toFixed(1) + "%"
: "0%";
console.log("Searches:", metrics.searches);
console.log("Unlocks:", metrics.unlocks);
console.log("Unique candidates:", metrics.candidatesSeen.size);
console.log("Conversion rate:", conversionRate);