ashby-mcp 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +138 -0
- package/dist/ashby-client.d.ts +28 -0
- package/dist/ashby-client.js +104 -0
- package/dist/ashby-client.js.map +1 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +13 -0
- package/dist/index.js.map +1 -0
- package/dist/server.d.ts +2 -0
- package/dist/server.js +480 -0
- package/dist/server.js.map +1 -0
- package/dist/types.d.ts +167 -0
- package/dist/types.js +3 -0
- package/dist/types.js.map +1 -0
- package/package.json +35 -0
package/README.md
ADDED
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
# ashby-mcp
|
|
2
|
+
|
|
3
|
+
MCP server for [Ashby](https://www.ashbyhq.com/) ATS — designed for candidate evaluation workflows.
|
|
4
|
+
|
|
5
|
+
This server exposes Ashby's recruiting data through the [Model Context Protocol](https://modelcontextprotocol.io/), letting AI agents review candidates, read application details, and write evaluation notes.
|
|
6
|
+
|
|
7
|
+
## Setup
|
|
8
|
+
|
|
9
|
+
### 1. Get an Ashby API Key
|
|
10
|
+
|
|
11
|
+
1. Go to your Ashby admin settings → Integrations → API Keys
|
|
12
|
+
2. Create a new API key with these permissions:
|
|
13
|
+
- `candidatesRead` — read candidate profiles, applications, notes, feedback
|
|
14
|
+
- `jobsRead` — read job listings and details
|
|
15
|
+
- `interviewsRead` — read interview stages and plans
|
|
16
|
+
- `candidatesWrite` — add notes, tags, and move application stages
|
|
17
|
+
|
|
18
|
+
### 2. Install
|
|
19
|
+
|
|
20
|
+
#### Option A: npx (recommended — no clone needed)
|
|
21
|
+
|
|
22
|
+
Just add the config below and it works immediately.
|
|
23
|
+
|
|
24
|
+
#### Option B: From source
|
|
25
|
+
|
|
26
|
+
```bash
|
|
27
|
+
git clone https://github.com/dewierwan/ashby-mcp.git && cd ashby-mcp
|
|
28
|
+
npm install
|
|
29
|
+
npm run build
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
### 3. Configure
|
|
33
|
+
|
|
34
|
+
#### Claude Code
|
|
35
|
+
|
|
36
|
+
Add to `~/.mcp.json`:
|
|
37
|
+
|
|
38
|
+
```json
|
|
39
|
+
{
|
|
40
|
+
"mcpServers": {
|
|
41
|
+
"ashby": {
|
|
42
|
+
"command": "npx",
|
|
43
|
+
"args": ["-y", "ashby-mcp"],
|
|
44
|
+
"env": {
|
|
45
|
+
"ASHBY_API_KEY": "your-api-key-here"
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
#### Claude Desktop
|
|
53
|
+
|
|
54
|
+
Add to `~/Library/Application Support/Claude/claude_desktop_config.json`:
|
|
55
|
+
|
|
56
|
+
```json
|
|
57
|
+
{
|
|
58
|
+
"mcpServers": {
|
|
59
|
+
"ashby": {
|
|
60
|
+
"command": "npx",
|
|
61
|
+
"args": ["-y", "ashby-mcp"],
|
|
62
|
+
"env": {
|
|
63
|
+
"ASHBY_API_KEY": "your-api-key-here"
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
## Tools
|
|
71
|
+
|
|
72
|
+
### Read tools
|
|
73
|
+
|
|
74
|
+
| Tool | Description |
|
|
75
|
+
|------|-------------|
|
|
76
|
+
| `ashby_list_jobs` | List open jobs with IDs, titles, department, location, status |
|
|
77
|
+
| `ashby_get_job_details` | Full job details including interview plan stages |
|
|
78
|
+
| `ashby_list_candidates_for_job` | List candidates/applications for a job with stage and status |
|
|
79
|
+
| `ashby_get_candidate` | Comprehensive candidate profile with all applications |
|
|
80
|
+
| `ashby_get_application_details` | Full application with stage history, feedback, evaluations |
|
|
81
|
+
| `ashby_get_candidate_notes` | All notes on a candidate |
|
|
82
|
+
| `ashby_search_candidates` | Search candidates by name or email |
|
|
83
|
+
| `ashby_list_interview_stages` | List all interview stages (pipeline reference) |
|
|
84
|
+
| `ashby_get_feedback` | Submitted feedback/scorecards for an application |
|
|
85
|
+
|
|
86
|
+
### Write tools
|
|
87
|
+
|
|
88
|
+
| Tool | Description |
|
|
89
|
+
|------|-------------|
|
|
90
|
+
| `ashby_add_candidate_note` | Add an evaluation note to a candidate |
|
|
91
|
+
| `ashby_move_application_stage` | Move an application to a different interview stage |
|
|
92
|
+
| `ashby_add_candidate_tag` | Tag a candidate (e.g. "Strong Hire") |
|
|
93
|
+
|
|
94
|
+
## Example Prompts
|
|
95
|
+
|
|
96
|
+
```
|
|
97
|
+
Show me all open engineering jobs.
|
|
98
|
+
|
|
99
|
+
List the candidates for the Senior Backend Engineer role and summarize where each one is in the pipeline.
|
|
100
|
+
|
|
101
|
+
Pull up the full profile for candidate Jane Smith — I want to see her resume info, application history, and any existing notes.
|
|
102
|
+
|
|
103
|
+
Review the feedback submitted for application abc-123 and summarize the interviewer evaluations.
|
|
104
|
+
|
|
105
|
+
Add a note to candidate xyz-456: "Strong technical skills demonstrated in system design round. Recommend advancing to final interview."
|
|
106
|
+
|
|
107
|
+
Move application abc-123 to the "Final Interview" stage.
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
## Testing
|
|
111
|
+
|
|
112
|
+
### Verify API connectivity
|
|
113
|
+
|
|
114
|
+
```bash
|
|
115
|
+
ASHBY_API_KEY=your-key npm run test-api
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
### Test with MCP Inspector
|
|
119
|
+
|
|
120
|
+
```bash
|
|
121
|
+
ASHBY_API_KEY=your-key npx @modelcontextprotocol/inspector node dist/index.js
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
### Run directly
|
|
125
|
+
|
|
126
|
+
```bash
|
|
127
|
+
ASHBY_API_KEY=your-key node dist/index.js
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
The server communicates over stdio — it will start silently and wait for MCP protocol messages.
|
|
131
|
+
|
|
132
|
+
## Development
|
|
133
|
+
|
|
134
|
+
```bash
|
|
135
|
+
npm install
|
|
136
|
+
npm run build # compile TypeScript to dist/
|
|
137
|
+
npm start # run the built server
|
|
138
|
+
```
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
export declare class AshbyClient {
|
|
2
|
+
private authHeader;
|
|
3
|
+
constructor();
|
|
4
|
+
/**
|
|
5
|
+
* Make a single request to an Ashby API endpoint.
|
|
6
|
+
* All Ashby endpoints are POST with JSON bodies.
|
|
7
|
+
*/
|
|
8
|
+
request<T>(endpoint: string, params?: Record<string, unknown>): Promise<T>;
|
|
9
|
+
/**
|
|
10
|
+
* Make a paginated request, returning a single page of results
|
|
11
|
+
* along with pagination metadata.
|
|
12
|
+
*/
|
|
13
|
+
requestList<T>(endpoint: string, params?: Record<string, unknown>): Promise<{
|
|
14
|
+
results: T[];
|
|
15
|
+
moreDataAvailable: boolean;
|
|
16
|
+
nextCursor?: string;
|
|
17
|
+
}>;
|
|
18
|
+
/**
|
|
19
|
+
* Async generator that yields items across all pages.
|
|
20
|
+
* Useful when you need to collect all results.
|
|
21
|
+
*/
|
|
22
|
+
paginate<T>(endpoint: string, params?: Record<string, unknown>): AsyncGenerator<T>;
|
|
23
|
+
}
|
|
24
|
+
export declare class AshbyApiError extends Error {
|
|
25
|
+
readonly httpStatus?: number | undefined;
|
|
26
|
+
readonly code?: string | undefined;
|
|
27
|
+
constructor(message: string, httpStatus?: number | undefined, code?: string | undefined);
|
|
28
|
+
}
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
const BASE_URL = "https://api.ashbyhq.com";
|
|
2
|
+
export class AshbyClient {
|
|
3
|
+
authHeader;
|
|
4
|
+
constructor() {
|
|
5
|
+
const apiKey = process.env.ASHBY_API_KEY;
|
|
6
|
+
if (!apiKey) {
|
|
7
|
+
throw new Error("ASHBY_API_KEY environment variable is required. " +
|
|
8
|
+
"Get an API key from your Ashby admin settings.");
|
|
9
|
+
}
|
|
10
|
+
this.authHeader = `Basic ${Buffer.from(`${apiKey}:`).toString("base64")}`;
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* Make a single request to an Ashby API endpoint.
|
|
14
|
+
* All Ashby endpoints are POST with JSON bodies.
|
|
15
|
+
*/
|
|
16
|
+
async request(endpoint, params) {
|
|
17
|
+
const url = `${BASE_URL}/${endpoint}`;
|
|
18
|
+
const response = await fetch(url, {
|
|
19
|
+
method: "POST",
|
|
20
|
+
headers: {
|
|
21
|
+
"Content-Type": "application/json",
|
|
22
|
+
Accept: "application/json",
|
|
23
|
+
Authorization: this.authHeader,
|
|
24
|
+
},
|
|
25
|
+
body: JSON.stringify(params ?? {}),
|
|
26
|
+
});
|
|
27
|
+
if (!response.ok) {
|
|
28
|
+
throw new AshbyApiError(`HTTP ${response.status}: ${response.statusText}`, response.status);
|
|
29
|
+
}
|
|
30
|
+
const data = (await response.json());
|
|
31
|
+
if (!data.success) {
|
|
32
|
+
const err = data;
|
|
33
|
+
const message = err.errorInfo?.message ??
|
|
34
|
+
err.errors?.join(", ") ??
|
|
35
|
+
"Unknown Ashby API error";
|
|
36
|
+
throw new AshbyApiError(message, 200, err.errorInfo?.code);
|
|
37
|
+
}
|
|
38
|
+
return data.results;
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Make a paginated request, returning a single page of results
|
|
42
|
+
* along with pagination metadata.
|
|
43
|
+
*/
|
|
44
|
+
async requestList(endpoint, params) {
|
|
45
|
+
const url = `${BASE_URL}/${endpoint}`;
|
|
46
|
+
const response = await fetch(url, {
|
|
47
|
+
method: "POST",
|
|
48
|
+
headers: {
|
|
49
|
+
"Content-Type": "application/json",
|
|
50
|
+
Accept: "application/json",
|
|
51
|
+
Authorization: this.authHeader,
|
|
52
|
+
},
|
|
53
|
+
body: JSON.stringify(params ?? {}),
|
|
54
|
+
});
|
|
55
|
+
if (!response.ok) {
|
|
56
|
+
throw new AshbyApiError(`HTTP ${response.status}: ${response.statusText}`, response.status);
|
|
57
|
+
}
|
|
58
|
+
const data = (await response.json());
|
|
59
|
+
if (!data.success) {
|
|
60
|
+
const err = data;
|
|
61
|
+
const message = err.errorInfo?.message ??
|
|
62
|
+
err.errors?.join(", ") ??
|
|
63
|
+
"Unknown Ashby API error";
|
|
64
|
+
throw new AshbyApiError(message, 200, err.errorInfo?.code);
|
|
65
|
+
}
|
|
66
|
+
const ok = data;
|
|
67
|
+
return {
|
|
68
|
+
results: ok.results,
|
|
69
|
+
moreDataAvailable: ok.moreDataAvailable,
|
|
70
|
+
nextCursor: ok.nextCursor,
|
|
71
|
+
};
|
|
72
|
+
}
|
|
73
|
+
/**
|
|
74
|
+
* Async generator that yields items across all pages.
|
|
75
|
+
* Useful when you need to collect all results.
|
|
76
|
+
*/
|
|
77
|
+
async *paginate(endpoint, params) {
|
|
78
|
+
let cursor;
|
|
79
|
+
let hasMore = true;
|
|
80
|
+
while (hasMore) {
|
|
81
|
+
const reqParams = { ...params };
|
|
82
|
+
if (cursor) {
|
|
83
|
+
reqParams.cursor = cursor;
|
|
84
|
+
}
|
|
85
|
+
const page = await this.requestList(endpoint, reqParams);
|
|
86
|
+
for (const item of page.results) {
|
|
87
|
+
yield item;
|
|
88
|
+
}
|
|
89
|
+
hasMore = page.moreDataAvailable;
|
|
90
|
+
cursor = page.nextCursor;
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
export class AshbyApiError extends Error {
|
|
95
|
+
httpStatus;
|
|
96
|
+
code;
|
|
97
|
+
constructor(message, httpStatus, code) {
|
|
98
|
+
super(message);
|
|
99
|
+
this.httpStatus = httpStatus;
|
|
100
|
+
this.code = code;
|
|
101
|
+
this.name = "AshbyApiError";
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
//# sourceMappingURL=ashby-client.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ashby-client.js","sourceRoot":"","sources":["../src/ashby-client.ts"],"names":[],"mappings":"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"}
|
package/dist/index.d.ts
ADDED
package/dist/index.js
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
3
|
+
import { createServer } from "./server.js";
|
|
4
|
+
async function main() {
|
|
5
|
+
const server = createServer();
|
|
6
|
+
const transport = new StdioServerTransport();
|
|
7
|
+
await server.connect(transport);
|
|
8
|
+
}
|
|
9
|
+
main().catch((error) => {
|
|
10
|
+
console.error("Fatal error:", error);
|
|
11
|
+
process.exit(1);
|
|
12
|
+
});
|
|
13
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AAEA,OAAO,EAAE,oBAAoB,EAAE,MAAM,2CAA2C,CAAC;AACjF,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAE3C,KAAK,UAAU,IAAI;IACjB,MAAM,MAAM,GAAG,YAAY,EAAE,CAAC;IAC9B,MAAM,SAAS,GAAG,IAAI,oBAAoB,EAAE,CAAC;IAC7C,MAAM,MAAM,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC;AAClC,CAAC;AAED,IAAI,EAAE,CAAC,KAAK,CAAC,CAAC,KAAK,EAAE,EAAE;IACrB,OAAO,CAAC,KAAK,CAAC,cAAc,EAAE,KAAK,CAAC,CAAC;IACrC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;AAClB,CAAC,CAAC,CAAC"}
|
package/dist/server.d.ts
ADDED
package/dist/server.js
ADDED
|
@@ -0,0 +1,480 @@
|
|
|
1
|
+
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
2
|
+
import { z } from "zod";
|
|
3
|
+
import { AshbyClient, AshbyApiError } from "./ashby-client.js";
|
|
4
|
+
function error(e) {
|
|
5
|
+
const msg = e instanceof AshbyApiError
|
|
6
|
+
? `Ashby API error: ${e.message}${e.code ? ` (code: ${e.code})` : ""}`
|
|
7
|
+
: `Error: ${e instanceof Error ? e.message : String(e)}`;
|
|
8
|
+
return { content: [{ type: "text", text: msg }] };
|
|
9
|
+
}
|
|
10
|
+
function json(data) {
|
|
11
|
+
return {
|
|
12
|
+
content: [{ type: "text", text: JSON.stringify(data, null, 2) }],
|
|
13
|
+
};
|
|
14
|
+
}
|
|
15
|
+
// ─── Server factory ─────────────────────────────────────────────────────────
|
|
16
|
+
export function createServer() {
|
|
17
|
+
const server = new McpServer({
|
|
18
|
+
name: "ashby",
|
|
19
|
+
version: "1.0.0",
|
|
20
|
+
});
|
|
21
|
+
const client = new AshbyClient();
|
|
22
|
+
// ── 1. ashby_list_jobs ──────────────────────────────────────────────────
|
|
23
|
+
server.tool("ashby_list_jobs", `List jobs from Ashby with their IDs, titles, department, location, and status.
|
|
24
|
+
|
|
25
|
+
Use this to discover what positions exist before looking at candidates.
|
|
26
|
+
Returns a paginated list — pass the next_cursor value to fetch more results.
|
|
27
|
+
The status filter is applied client-side since the Ashby API returns all jobs.
|
|
28
|
+
|
|
29
|
+
Response: items[] (id, title, status, locationId, departmentId), has_more, next_cursor.`, {
|
|
30
|
+
status: z
|
|
31
|
+
.enum(["Open", "Closed", "Archived", "Draft", "All"])
|
|
32
|
+
.default("Open")
|
|
33
|
+
.describe('Filter by job status. Use "All" to return every job. Defaults to "Open".'),
|
|
34
|
+
limit: z
|
|
35
|
+
.number()
|
|
36
|
+
.min(1)
|
|
37
|
+
.max(100)
|
|
38
|
+
.default(25)
|
|
39
|
+
.describe("Max results per page (1-100). Defaults to 25."),
|
|
40
|
+
cursor: z
|
|
41
|
+
.string()
|
|
42
|
+
.optional()
|
|
43
|
+
.describe("Pagination cursor from a previous response."),
|
|
44
|
+
}, async ({ status, limit, cursor }) => {
|
|
45
|
+
try {
|
|
46
|
+
const params = { limit };
|
|
47
|
+
if (cursor)
|
|
48
|
+
params.cursor = cursor;
|
|
49
|
+
const page = await client.requestList("job.list", params);
|
|
50
|
+
const filtered = status === "All"
|
|
51
|
+
? page.results
|
|
52
|
+
: page.results.filter((j) => j.status === status);
|
|
53
|
+
return json({
|
|
54
|
+
items: filtered.map((j) => ({
|
|
55
|
+
id: j.id,
|
|
56
|
+
title: j.title,
|
|
57
|
+
status: j.status,
|
|
58
|
+
locationId: j.locationId ?? null,
|
|
59
|
+
departmentId: j.departmentId ?? null,
|
|
60
|
+
createdAt: j.createdAt,
|
|
61
|
+
updatedAt: j.updatedAt,
|
|
62
|
+
})),
|
|
63
|
+
has_more: page.moreDataAvailable,
|
|
64
|
+
next_cursor: page.nextCursor ?? null,
|
|
65
|
+
});
|
|
66
|
+
}
|
|
67
|
+
catch (e) {
|
|
68
|
+
return error(e);
|
|
69
|
+
}
|
|
70
|
+
});
|
|
71
|
+
// ── 2. ashby_get_job_details ────────────────────────────────────────────
|
|
72
|
+
server.tool("ashby_get_job_details", `Get full details for a specific job including its interview plan stages.
|
|
73
|
+
|
|
74
|
+
Use this after ashby_list_jobs to understand a position's requirements and hiring pipeline.
|
|
75
|
+
Fetches the job, then resolves its interview plan stages automatically.
|
|
76
|
+
|
|
77
|
+
Response: job (id, title, status, hiringTeam, customFields, locationId, departmentId), interview_stages[] (id, title, type, order).`, {
|
|
78
|
+
job_id: z.string().describe("The job ID (UUID) to fetch details for."),
|
|
79
|
+
}, async ({ job_id }) => {
|
|
80
|
+
try {
|
|
81
|
+
const job = await client.request("job.info", { id: job_id });
|
|
82
|
+
// Fetch stages for the job's interview plan(s)
|
|
83
|
+
const planIds = job.interviewPlanIds ?? (job.defaultInterviewPlanId ? [job.defaultInterviewPlanId] : []);
|
|
84
|
+
const stageResults = await Promise.all(planIds.map((planId) => client
|
|
85
|
+
.requestList("interviewStage.list", { interviewPlanId: planId })
|
|
86
|
+
.then((r) => r.results)
|
|
87
|
+
.catch(() => [])));
|
|
88
|
+
const stages = stageResults.flat();
|
|
89
|
+
return json({
|
|
90
|
+
job: {
|
|
91
|
+
id: job.id,
|
|
92
|
+
title: job.title,
|
|
93
|
+
status: job.status,
|
|
94
|
+
employmentType: job.employmentType ?? null,
|
|
95
|
+
locationId: job.locationId ?? null,
|
|
96
|
+
departmentId: job.departmentId ?? null,
|
|
97
|
+
hiringTeam: (job.hiringTeam ?? []).map((m) => ({
|
|
98
|
+
name: `${m.firstName} ${m.lastName}`,
|
|
99
|
+
email: m.email,
|
|
100
|
+
role: m.role,
|
|
101
|
+
})),
|
|
102
|
+
customFields: job.customFields ?? [],
|
|
103
|
+
createdAt: job.createdAt,
|
|
104
|
+
updatedAt: job.updatedAt,
|
|
105
|
+
},
|
|
106
|
+
interview_stages: stages
|
|
107
|
+
.sort((a, b) => a.orderInInterviewPlan - b.orderInInterviewPlan)
|
|
108
|
+
.map((s) => ({
|
|
109
|
+
id: s.id,
|
|
110
|
+
title: s.title,
|
|
111
|
+
type: s.type,
|
|
112
|
+
order: s.orderInInterviewPlan,
|
|
113
|
+
})),
|
|
114
|
+
});
|
|
115
|
+
}
|
|
116
|
+
catch (e) {
|
|
117
|
+
return error(e);
|
|
118
|
+
}
|
|
119
|
+
});
|
|
120
|
+
// ── 3. ashby_list_candidates_for_job ────────────────────────────────────
|
|
121
|
+
server.tool("ashby_list_candidates_for_job", `List all candidates/applications for a specific job.
|
|
122
|
+
|
|
123
|
+
Returns each application with the candidate's name, current interview stage, and status.
|
|
124
|
+
Use this to see the pipeline for a job. Pass next_cursor to paginate.
|
|
125
|
+
|
|
126
|
+
Response: items[] (application_id, candidate_id, candidate_name, status, current_stage, source, createdAt), has_more, next_cursor.`, {
|
|
127
|
+
job_id: z.string().describe("The job ID (UUID) to list candidates for."),
|
|
128
|
+
limit: z
|
|
129
|
+
.number()
|
|
130
|
+
.min(1)
|
|
131
|
+
.max(100)
|
|
132
|
+
.default(25)
|
|
133
|
+
.describe("Max results per page (1-100). Defaults to 25."),
|
|
134
|
+
cursor: z
|
|
135
|
+
.string()
|
|
136
|
+
.optional()
|
|
137
|
+
.describe("Pagination cursor from a previous response."),
|
|
138
|
+
}, async ({ job_id, limit, cursor }) => {
|
|
139
|
+
try {
|
|
140
|
+
const params = { jobId: job_id, limit };
|
|
141
|
+
if (cursor)
|
|
142
|
+
params.cursor = cursor;
|
|
143
|
+
const page = await client.requestList("application.list", params);
|
|
144
|
+
return json({
|
|
145
|
+
items: page.results.map((app) => ({
|
|
146
|
+
application_id: app.id,
|
|
147
|
+
candidate_id: app.candidate.id,
|
|
148
|
+
candidate_name: app.candidate.name,
|
|
149
|
+
candidate_email: app.candidate.primaryEmailAddress?.value ?? null,
|
|
150
|
+
status: app.status,
|
|
151
|
+
current_stage: app.currentInterviewStage
|
|
152
|
+
? {
|
|
153
|
+
id: app.currentInterviewStage.id,
|
|
154
|
+
title: app.currentInterviewStage.title,
|
|
155
|
+
type: app.currentInterviewStage.type,
|
|
156
|
+
}
|
|
157
|
+
: null,
|
|
158
|
+
source: app.source?.title ?? null,
|
|
159
|
+
createdAt: app.createdAt,
|
|
160
|
+
})),
|
|
161
|
+
has_more: page.moreDataAvailable,
|
|
162
|
+
next_cursor: page.nextCursor ?? null,
|
|
163
|
+
});
|
|
164
|
+
}
|
|
165
|
+
catch (e) {
|
|
166
|
+
return error(e);
|
|
167
|
+
}
|
|
168
|
+
});
|
|
169
|
+
// ── 4. ashby_get_candidate ──────────────────────────────────────────────
|
|
170
|
+
server.tool("ashby_get_candidate", `Get a comprehensive candidate profile.
|
|
171
|
+
|
|
172
|
+
Returns everything needed to evaluate a candidate: name, contact info, social links, tags,
|
|
173
|
+
resume/file handles, source, and all their applications with current stages.
|
|
174
|
+
Fetches the candidate profile, then resolves each application for full details.
|
|
175
|
+
|
|
176
|
+
Response: candidate (id, name, email, phone, socialLinks, tags, source, profileUrl, fileHandles), applications[] (id, status, job, current_stage, source, hiringTeam).`, {
|
|
177
|
+
candidate_id: z.string().describe("The candidate ID (UUID) to fetch."),
|
|
178
|
+
}, async ({ candidate_id }) => {
|
|
179
|
+
try {
|
|
180
|
+
const candidate = await client.request("candidate.info", { id: candidate_id });
|
|
181
|
+
// Resolve full application details from applicationIds
|
|
182
|
+
let applications = [];
|
|
183
|
+
const appIds = candidate.applicationIds ?? [];
|
|
184
|
+
if (appIds.length > 0) {
|
|
185
|
+
const appResults = await Promise.all(appIds.map((appId) => client
|
|
186
|
+
.request("application.info", { applicationId: appId })
|
|
187
|
+
.catch(() => null)));
|
|
188
|
+
applications = appResults.filter((a) => a !== null);
|
|
189
|
+
}
|
|
190
|
+
return json({
|
|
191
|
+
candidate: {
|
|
192
|
+
id: candidate.id,
|
|
193
|
+
name: candidate.name,
|
|
194
|
+
email: candidate.primaryEmailAddress?.value ?? null,
|
|
195
|
+
phone: candidate.primaryPhoneNumber?.value ?? null,
|
|
196
|
+
socialLinks: candidate.socialLinks ?? [],
|
|
197
|
+
tags: (candidate.tags ?? []).map((t) => ({ id: t.id, title: t.title })),
|
|
198
|
+
source: candidate.source?.title ?? null,
|
|
199
|
+
customFields: candidate.customFields ?? [],
|
|
200
|
+
fileHandles: candidate.fileHandles ?? [],
|
|
201
|
+
profileUrl: candidate.profileUrl ?? null,
|
|
202
|
+
},
|
|
203
|
+
applications: applications.map((app) => ({
|
|
204
|
+
id: app.id,
|
|
205
|
+
status: app.status,
|
|
206
|
+
job: { id: app.job.id, title: app.job.title },
|
|
207
|
+
current_stage: app.currentInterviewStage
|
|
208
|
+
? {
|
|
209
|
+
id: app.currentInterviewStage.id,
|
|
210
|
+
title: app.currentInterviewStage.title,
|
|
211
|
+
type: app.currentInterviewStage.type,
|
|
212
|
+
}
|
|
213
|
+
: null,
|
|
214
|
+
source: app.source?.title ?? null,
|
|
215
|
+
hiringTeam: (app.hiringTeam ?? []).map((m) => ({
|
|
216
|
+
name: `${m.firstName} ${m.lastName}`,
|
|
217
|
+
email: m.email,
|
|
218
|
+
role: m.role,
|
|
219
|
+
})),
|
|
220
|
+
createdAt: app.createdAt,
|
|
221
|
+
})),
|
|
222
|
+
});
|
|
223
|
+
}
|
|
224
|
+
catch (e) {
|
|
225
|
+
return error(e);
|
|
226
|
+
}
|
|
227
|
+
});
|
|
228
|
+
// ── 5. ashby_get_application_details ────────────────────────────────────
|
|
229
|
+
server.tool("ashby_get_application_details", `Get full application details including stage history, hiring team, feedback, and criteria evaluations.
|
|
230
|
+
|
|
231
|
+
Use this to deep-dive into a specific application. Fires four API calls concurrently for speed.
|
|
232
|
+
|
|
233
|
+
Response: application (id, status, candidate, job, current_stage, hiringTeam, source, customFields), stage_history[], criteria_evaluations[], feedback[].`, {
|
|
234
|
+
application_id: z.string().describe("The application ID (UUID) to fetch."),
|
|
235
|
+
}, async ({ application_id }) => {
|
|
236
|
+
try {
|
|
237
|
+
const [appInfo, historyPage, criteriaPage, feedbackPage] = await Promise.all([
|
|
238
|
+
client.request("application.info", { applicationId: application_id }),
|
|
239
|
+
client
|
|
240
|
+
.requestList("application.listHistory", { applicationId: application_id })
|
|
241
|
+
.catch(() => ({ results: [], moreDataAvailable: false })),
|
|
242
|
+
client
|
|
243
|
+
.requestList("application.listCriteriaEvaluations", { applicationId: application_id })
|
|
244
|
+
.catch(() => ({ results: [], moreDataAvailable: false })),
|
|
245
|
+
client
|
|
246
|
+
.requestList("applicationFeedback.list", { applicationId: application_id })
|
|
247
|
+
.catch(() => ({ results: [], moreDataAvailable: false })),
|
|
248
|
+
]);
|
|
249
|
+
return json({
|
|
250
|
+
application: {
|
|
251
|
+
id: appInfo.id,
|
|
252
|
+
status: appInfo.status,
|
|
253
|
+
candidate: {
|
|
254
|
+
id: appInfo.candidate.id,
|
|
255
|
+
name: appInfo.candidate.name,
|
|
256
|
+
email: appInfo.candidate.primaryEmailAddress?.value ?? null,
|
|
257
|
+
},
|
|
258
|
+
job: { id: appInfo.job.id, title: appInfo.job.title },
|
|
259
|
+
current_stage: appInfo.currentInterviewStage
|
|
260
|
+
? {
|
|
261
|
+
id: appInfo.currentInterviewStage.id,
|
|
262
|
+
title: appInfo.currentInterviewStage.title,
|
|
263
|
+
type: appInfo.currentInterviewStage.type,
|
|
264
|
+
order: appInfo.currentInterviewStage.orderInInterviewPlan,
|
|
265
|
+
}
|
|
266
|
+
: null,
|
|
267
|
+
hiringTeam: (appInfo.hiringTeam ?? []).map((m) => ({
|
|
268
|
+
name: `${m.firstName} ${m.lastName}`,
|
|
269
|
+
email: m.email,
|
|
270
|
+
role: m.role,
|
|
271
|
+
})),
|
|
272
|
+
source: appInfo.source?.title ?? null,
|
|
273
|
+
customFields: appInfo.customFields ?? [],
|
|
274
|
+
resumeFileHandle: appInfo.resumeFileHandle ?? null,
|
|
275
|
+
createdAt: appInfo.createdAt,
|
|
276
|
+
updatedAt: appInfo.updatedAt,
|
|
277
|
+
},
|
|
278
|
+
stage_history: historyPage.results.map((h) => ({
|
|
279
|
+
stageId: h.stageId,
|
|
280
|
+
title: h.title,
|
|
281
|
+
enteredAt: h.enteredStageAt,
|
|
282
|
+
leftAt: h.leftStageAt ?? null,
|
|
283
|
+
stageNumber: h.stageNumber,
|
|
284
|
+
})),
|
|
285
|
+
criteria_evaluations: criteriaPage.results,
|
|
286
|
+
feedback: feedbackPage.results,
|
|
287
|
+
});
|
|
288
|
+
}
|
|
289
|
+
catch (e) {
|
|
290
|
+
return error(e);
|
|
291
|
+
}
|
|
292
|
+
});
|
|
293
|
+
// ── 6. ashby_get_candidate_notes ────────────────────────────────────────
|
|
294
|
+
server.tool("ashby_get_candidate_notes", `List all notes on a candidate.
|
|
295
|
+
|
|
296
|
+
Use this to see existing evaluation notes or comments left by the hiring team.
|
|
297
|
+
|
|
298
|
+
Response: notes[] (id, content, createdAt, author).`, {
|
|
299
|
+
candidate_id: z.string().describe("The candidate ID (UUID) to fetch notes for."),
|
|
300
|
+
}, async ({ candidate_id }) => {
|
|
301
|
+
try {
|
|
302
|
+
const page = await client.requestList("candidate.listNotes", { candidateId: candidate_id, limit: 100 });
|
|
303
|
+
return json({
|
|
304
|
+
notes: page.results.map((n) => ({
|
|
305
|
+
id: n.id,
|
|
306
|
+
content: n.content,
|
|
307
|
+
createdAt: n.createdAt,
|
|
308
|
+
author: n.author
|
|
309
|
+
? `${n.author.firstName} ${n.author.lastName} (${n.author.email})`
|
|
310
|
+
: null,
|
|
311
|
+
})),
|
|
312
|
+
has_more: page.moreDataAvailable,
|
|
313
|
+
next_cursor: page.nextCursor ?? null,
|
|
314
|
+
});
|
|
315
|
+
}
|
|
316
|
+
catch (e) {
|
|
317
|
+
return error(e);
|
|
318
|
+
}
|
|
319
|
+
});
|
|
320
|
+
// ── 7. ashby_search_candidates ──────────────────────────────────────────
|
|
321
|
+
server.tool("ashby_search_candidates", `Search for candidates by name or email.
|
|
322
|
+
|
|
323
|
+
Use this when you know a candidate's name or email but not their ID.
|
|
324
|
+
Both name and email use AND logic if both provided. Max 100 results.
|
|
325
|
+
|
|
326
|
+
Response: candidates[] (id, name, email, phone).`, {
|
|
327
|
+
query: z.string().describe("Candidate name to search for."),
|
|
328
|
+
email: z
|
|
329
|
+
.string()
|
|
330
|
+
.optional()
|
|
331
|
+
.describe("Optional email to narrow search (AND logic with name)."),
|
|
332
|
+
limit: z
|
|
333
|
+
.number()
|
|
334
|
+
.min(1)
|
|
335
|
+
.max(100)
|
|
336
|
+
.default(25)
|
|
337
|
+
.describe("Max results (1-100). Defaults to 25."),
|
|
338
|
+
}, async ({ query, email, limit }) => {
|
|
339
|
+
try {
|
|
340
|
+
const params = { name: query, limit };
|
|
341
|
+
if (email)
|
|
342
|
+
params.email = email;
|
|
343
|
+
const results = await client.request("candidate.search", params);
|
|
344
|
+
return json({
|
|
345
|
+
candidates: results.map((c) => ({
|
|
346
|
+
id: c.id,
|
|
347
|
+
name: c.name,
|
|
348
|
+
email: c.primaryEmailAddress?.value ?? null,
|
|
349
|
+
phone: c.primaryPhoneNumber?.value ?? null,
|
|
350
|
+
})),
|
|
351
|
+
});
|
|
352
|
+
}
|
|
353
|
+
catch (e) {
|
|
354
|
+
return error(e);
|
|
355
|
+
}
|
|
356
|
+
});
|
|
357
|
+
// ── 8. ashby_list_interview_stages ──────────────────────────────────────
|
|
358
|
+
server.tool("ashby_list_interview_stages", `List all interview stages across all interview plans.
|
|
359
|
+
|
|
360
|
+
Use this to understand the hiring pipeline and get stage IDs for ashby_move_application_stage.
|
|
361
|
+
Fetches all interview plans, then resolves stages for each.
|
|
362
|
+
|
|
363
|
+
Response: plans[] (plan_id, plan_title, stages[] (id, title, type, order)).`, {}, async () => {
|
|
364
|
+
try {
|
|
365
|
+
const planPage = await client.requestList("interviewPlan.list", {});
|
|
366
|
+
const plansWithStages = await Promise.all(planPage.results.map(async (plan) => {
|
|
367
|
+
const stagePage = await client
|
|
368
|
+
.requestList("interviewStage.list", { interviewPlanId: plan.id })
|
|
369
|
+
.catch(() => ({ results: [], moreDataAvailable: false }));
|
|
370
|
+
return {
|
|
371
|
+
plan_id: plan.id,
|
|
372
|
+
plan_title: plan.title,
|
|
373
|
+
stages: stagePage.results
|
|
374
|
+
.sort((a, b) => a.orderInInterviewPlan - b.orderInInterviewPlan)
|
|
375
|
+
.map((s) => ({
|
|
376
|
+
id: s.id,
|
|
377
|
+
title: s.title,
|
|
378
|
+
type: s.type,
|
|
379
|
+
order: s.orderInInterviewPlan,
|
|
380
|
+
})),
|
|
381
|
+
};
|
|
382
|
+
}));
|
|
383
|
+
return json({ plans: plansWithStages });
|
|
384
|
+
}
|
|
385
|
+
catch (e) {
|
|
386
|
+
return error(e);
|
|
387
|
+
}
|
|
388
|
+
});
|
|
389
|
+
// ── 9. ashby_get_feedback ───────────────────────────────────────────────
|
|
390
|
+
server.tool("ashby_get_feedback", `Get submitted feedback/scorecards for an application.
|
|
391
|
+
|
|
392
|
+
Use this to review interviewer evaluations and scores.
|
|
393
|
+
|
|
394
|
+
Response: feedback[] (form definition with sections/fields, submitted values).`, {
|
|
395
|
+
application_id: z.string().describe("The application ID (UUID) to fetch feedback for."),
|
|
396
|
+
}, async ({ application_id }) => {
|
|
397
|
+
try {
|
|
398
|
+
const page = await client.requestList("applicationFeedback.list", { applicationId: application_id });
|
|
399
|
+
return json({
|
|
400
|
+
feedback: page.results,
|
|
401
|
+
has_more: page.moreDataAvailable,
|
|
402
|
+
next_cursor: page.nextCursor ?? null,
|
|
403
|
+
});
|
|
404
|
+
}
|
|
405
|
+
catch (e) {
|
|
406
|
+
return error(e);
|
|
407
|
+
}
|
|
408
|
+
});
|
|
409
|
+
// ── 10. ashby_add_candidate_note ────────────────────────────────────────
|
|
410
|
+
server.tool("ashby_add_candidate_note", `Add an evaluation note to a candidate.
|
|
411
|
+
|
|
412
|
+
Use this to record your assessment or recommendations. The note will be visible to the hiring team in Ashby.
|
|
413
|
+
|
|
414
|
+
Response: note_id, confirmation message.`, {
|
|
415
|
+
candidate_id: z.string().describe("The candidate ID (UUID) to add a note to."),
|
|
416
|
+
note: z.string().describe("The note content (plain text). Visible to the hiring team."),
|
|
417
|
+
}, async ({ candidate_id, note }) => {
|
|
418
|
+
try {
|
|
419
|
+
const result = await client.request("candidate.createNote", { candidateId: candidate_id, note });
|
|
420
|
+
return json({
|
|
421
|
+
note_id: result.id,
|
|
422
|
+
message: `Note added to candidate ${candidate_id}.`,
|
|
423
|
+
});
|
|
424
|
+
}
|
|
425
|
+
catch (e) {
|
|
426
|
+
return error(e);
|
|
427
|
+
}
|
|
428
|
+
});
|
|
429
|
+
// ── 11. ashby_move_application_stage ────────────────────────────────────
|
|
430
|
+
server.tool("ashby_move_application_stage", `Move an application to a different interview stage.
|
|
431
|
+
|
|
432
|
+
Use this to advance a candidate through the pipeline. Get stage IDs from ashby_list_interview_stages.
|
|
433
|
+
Moving to an "Archived" type stage requires an archive reason — use only for active transitions.
|
|
434
|
+
|
|
435
|
+
Response: updated application with new current_stage.`, {
|
|
436
|
+
application_id: z.string().describe("The application ID (UUID) to move."),
|
|
437
|
+
stage_id: z.string().describe("The target interview stage ID (UUID). Get from ashby_list_interview_stages."),
|
|
438
|
+
}, async ({ application_id, stage_id }) => {
|
|
439
|
+
try {
|
|
440
|
+
const result = await client.request("application.changeStage", { applicationId: application_id, interviewStageId: stage_id });
|
|
441
|
+
return json({
|
|
442
|
+
application_id: result.id,
|
|
443
|
+
new_stage: result.currentInterviewStage
|
|
444
|
+
? {
|
|
445
|
+
id: result.currentInterviewStage.id,
|
|
446
|
+
title: result.currentInterviewStage.title,
|
|
447
|
+
type: result.currentInterviewStage.type,
|
|
448
|
+
}
|
|
449
|
+
: null,
|
|
450
|
+
status: result.status,
|
|
451
|
+
message: `Application moved to stage "${result.currentInterviewStage?.title ?? stage_id}".`,
|
|
452
|
+
});
|
|
453
|
+
}
|
|
454
|
+
catch (e) {
|
|
455
|
+
return error(e);
|
|
456
|
+
}
|
|
457
|
+
});
|
|
458
|
+
// ── 12. ashby_add_candidate_tag ─────────────────────────────────────────
|
|
459
|
+
server.tool("ashby_add_candidate_tag", `Tag a candidate with a label (e.g. "Strong Hire", "Needs Review").
|
|
460
|
+
|
|
461
|
+
Use this to categorize candidates during evaluation. You need the tag ID from Ashby admin settings.
|
|
462
|
+
|
|
463
|
+
Response: confirmation message.`, {
|
|
464
|
+
candidate_id: z.string().describe("The candidate ID (UUID) to tag."),
|
|
465
|
+
tag_id: z.string().describe("The tag ID (UUID). Tags are configured in Ashby admin settings."),
|
|
466
|
+
}, async ({ candidate_id, tag_id }) => {
|
|
467
|
+
try {
|
|
468
|
+
await client.request("candidate.addTag", {
|
|
469
|
+
candidateId: candidate_id,
|
|
470
|
+
tagId: tag_id,
|
|
471
|
+
});
|
|
472
|
+
return json({ message: `Tag ${tag_id} added to candidate ${candidate_id}.` });
|
|
473
|
+
}
|
|
474
|
+
catch (e) {
|
|
475
|
+
return error(e);
|
|
476
|
+
}
|
|
477
|
+
});
|
|
478
|
+
return server;
|
|
479
|
+
}
|
|
480
|
+
//# sourceMappingURL=server.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"server.js","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"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"}
|
package/dist/types.d.ts
ADDED
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
export interface AshbySuccessResponse<T> {
|
|
2
|
+
success: true;
|
|
3
|
+
results: T;
|
|
4
|
+
}
|
|
5
|
+
export interface AshbyPaginatedResponse<T> {
|
|
6
|
+
success: true;
|
|
7
|
+
results: T[];
|
|
8
|
+
moreDataAvailable: boolean;
|
|
9
|
+
nextCursor?: string;
|
|
10
|
+
}
|
|
11
|
+
export interface AshbyErrorResponse {
|
|
12
|
+
success: false;
|
|
13
|
+
errors?: string[];
|
|
14
|
+
errorInfo?: {
|
|
15
|
+
code: string;
|
|
16
|
+
message?: string;
|
|
17
|
+
requestId?: string;
|
|
18
|
+
};
|
|
19
|
+
}
|
|
20
|
+
export type AshbyResponse<T> = AshbySuccessResponse<T> | AshbyErrorResponse;
|
|
21
|
+
export type AshbyListResponse<T> = AshbyPaginatedResponse<T> | AshbyErrorResponse;
|
|
22
|
+
export interface ContactInfo {
|
|
23
|
+
value: string;
|
|
24
|
+
type: string;
|
|
25
|
+
isPrimary: boolean;
|
|
26
|
+
}
|
|
27
|
+
export interface CustomField {
|
|
28
|
+
id: string;
|
|
29
|
+
isPrivate: boolean;
|
|
30
|
+
title: string;
|
|
31
|
+
value: unknown;
|
|
32
|
+
valueLabel?: string | string[];
|
|
33
|
+
}
|
|
34
|
+
export interface HiringTeamMember {
|
|
35
|
+
userId: string;
|
|
36
|
+
firstName: string;
|
|
37
|
+
lastName: string;
|
|
38
|
+
email: string;
|
|
39
|
+
role: string;
|
|
40
|
+
}
|
|
41
|
+
export interface SourceInfo {
|
|
42
|
+
id: string;
|
|
43
|
+
title: string;
|
|
44
|
+
isArchived: boolean;
|
|
45
|
+
}
|
|
46
|
+
export interface InterviewStageRef {
|
|
47
|
+
id: string;
|
|
48
|
+
title: string;
|
|
49
|
+
type: string;
|
|
50
|
+
orderInInterviewPlan: number;
|
|
51
|
+
interviewPlanId?: string;
|
|
52
|
+
}
|
|
53
|
+
export interface JobRef {
|
|
54
|
+
id: string;
|
|
55
|
+
title: string;
|
|
56
|
+
locationId?: string;
|
|
57
|
+
departmentId?: string;
|
|
58
|
+
}
|
|
59
|
+
export interface CandidateRef {
|
|
60
|
+
id: string;
|
|
61
|
+
name: string;
|
|
62
|
+
primaryEmailAddress?: ContactInfo;
|
|
63
|
+
primaryPhoneNumber?: ContactInfo;
|
|
64
|
+
}
|
|
65
|
+
export interface TagRef {
|
|
66
|
+
id: string;
|
|
67
|
+
title: string;
|
|
68
|
+
isArchived: boolean;
|
|
69
|
+
}
|
|
70
|
+
export interface FileHandle {
|
|
71
|
+
id: string;
|
|
72
|
+
name: string;
|
|
73
|
+
handle: string;
|
|
74
|
+
}
|
|
75
|
+
export interface Job {
|
|
76
|
+
id: string;
|
|
77
|
+
title: string;
|
|
78
|
+
status: string;
|
|
79
|
+
employmentType?: string;
|
|
80
|
+
confidential?: boolean;
|
|
81
|
+
locationId?: string;
|
|
82
|
+
departmentId?: string;
|
|
83
|
+
defaultInterviewPlanId?: string;
|
|
84
|
+
interviewPlanIds?: string[];
|
|
85
|
+
customFields?: CustomField[];
|
|
86
|
+
hiringTeam?: HiringTeamMember[];
|
|
87
|
+
createdAt: string;
|
|
88
|
+
updatedAt: string;
|
|
89
|
+
openedAt?: string;
|
|
90
|
+
closedAt?: string;
|
|
91
|
+
}
|
|
92
|
+
export interface InterviewStage {
|
|
93
|
+
id: string;
|
|
94
|
+
title: string;
|
|
95
|
+
type: string;
|
|
96
|
+
orderInInterviewPlan: number;
|
|
97
|
+
interviewStageGroupId?: string;
|
|
98
|
+
}
|
|
99
|
+
export interface InterviewPlan {
|
|
100
|
+
id: string;
|
|
101
|
+
title: string;
|
|
102
|
+
createdAt: string;
|
|
103
|
+
updatedAt: string;
|
|
104
|
+
}
|
|
105
|
+
export interface Application {
|
|
106
|
+
id: string;
|
|
107
|
+
createdAt: string;
|
|
108
|
+
updatedAt: string;
|
|
109
|
+
status: "Hired" | "Archived" | "Active" | "Lead";
|
|
110
|
+
candidate: CandidateRef;
|
|
111
|
+
currentInterviewStage?: InterviewStageRef;
|
|
112
|
+
job: JobRef;
|
|
113
|
+
source?: SourceInfo;
|
|
114
|
+
archiveReason?: {
|
|
115
|
+
id: string;
|
|
116
|
+
text: string;
|
|
117
|
+
reasonType: string;
|
|
118
|
+
};
|
|
119
|
+
hiringTeam?: HiringTeamMember[];
|
|
120
|
+
customFields?: CustomField[];
|
|
121
|
+
resumeFileHandle?: FileHandle;
|
|
122
|
+
applicationHistory?: ApplicationHistoryEntry[];
|
|
123
|
+
}
|
|
124
|
+
export interface ApplicationHistoryEntry {
|
|
125
|
+
id: string;
|
|
126
|
+
stageId: string;
|
|
127
|
+
title: string;
|
|
128
|
+
enteredStageAt: string;
|
|
129
|
+
leftStageAt?: string;
|
|
130
|
+
stageNumber: number;
|
|
131
|
+
}
|
|
132
|
+
export interface CriteriaEvaluation {
|
|
133
|
+
[key: string]: unknown;
|
|
134
|
+
}
|
|
135
|
+
export interface ApplicationFeedback {
|
|
136
|
+
[key: string]: unknown;
|
|
137
|
+
}
|
|
138
|
+
export interface Candidate {
|
|
139
|
+
id: string;
|
|
140
|
+
name: string;
|
|
141
|
+
primaryEmailAddress?: ContactInfo;
|
|
142
|
+
primaryPhoneNumber?: ContactInfo;
|
|
143
|
+
emailAddresses?: ContactInfo[];
|
|
144
|
+
phoneNumbers?: ContactInfo[];
|
|
145
|
+
socialLinks?: {
|
|
146
|
+
type: string;
|
|
147
|
+
url: string;
|
|
148
|
+
}[];
|
|
149
|
+
tags?: TagRef[];
|
|
150
|
+
source?: SourceInfo;
|
|
151
|
+
customFields?: CustomField[];
|
|
152
|
+
applicationIds?: string[];
|
|
153
|
+
fileHandles?: FileHandle[];
|
|
154
|
+
profileUrl?: string;
|
|
155
|
+
}
|
|
156
|
+
export interface CandidateNote {
|
|
157
|
+
id: string;
|
|
158
|
+
createdAt: string;
|
|
159
|
+
isPrivate: boolean;
|
|
160
|
+
content: string;
|
|
161
|
+
author?: {
|
|
162
|
+
id: string;
|
|
163
|
+
firstName: string;
|
|
164
|
+
lastName: string;
|
|
165
|
+
email: string;
|
|
166
|
+
};
|
|
167
|
+
}
|
package/dist/types.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,+EAA+E"}
|
package/package.json
ADDED
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "ashby-mcp",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "MCP server for Ashby ATS — candidate evaluation workflow",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "dist/index.js",
|
|
7
|
+
"bin": {
|
|
8
|
+
"ashby-mcp": "dist/index.js"
|
|
9
|
+
},
|
|
10
|
+
"files": [
|
|
11
|
+
"dist",
|
|
12
|
+
"README.md"
|
|
13
|
+
],
|
|
14
|
+
"repository": {
|
|
15
|
+
"type": "git",
|
|
16
|
+
"url": "https://github.com/dewierwan/ashby-mcp"
|
|
17
|
+
},
|
|
18
|
+
"scripts": {
|
|
19
|
+
"build": "tsc",
|
|
20
|
+
"prepublishOnly": "npm run build",
|
|
21
|
+
"start": "node dist/index.js",
|
|
22
|
+
"test-api": "tsx scripts/test-api.ts"
|
|
23
|
+
},
|
|
24
|
+
"keywords": ["mcp", "ashby", "ats", "recruiting", "hiring", "applicant-tracking"],
|
|
25
|
+
"license": "MIT",
|
|
26
|
+
"dependencies": {
|
|
27
|
+
"@modelcontextprotocol/sdk": "^1.12.1",
|
|
28
|
+
"zod": "^3.24.2"
|
|
29
|
+
},
|
|
30
|
+
"devDependencies": {
|
|
31
|
+
"@types/node": "^22.13.4",
|
|
32
|
+
"tsx": "^4.19.3",
|
|
33
|
+
"typescript": "^5.7.3"
|
|
34
|
+
}
|
|
35
|
+
}
|