post_job
Publish job listings to the ClawHire hiring platform to reach candidates through search results, matching recommendations, and WonderCV channels.
Instructions
发布职位到 ClawHire 招聘市场。
职位将同时出现在:
ClawHire 雇主端搜索结果
候选人的匹配推荐(如果符合其期望)
WonderCV 相关渠道
提示:
设置 require_ai_fluency=true 可优先展示给 AI-Agent 熟练的候选人
工作年限会自动转换为天数存储(WonderCV 内部格式)
职位默认有效期30天
Input Schema
TableJSON Schema
| Name | Required | Description | Default |
|---|---|---|---|
| session_id | Yes | 会话 ID,从 register_company 获取 | |
| job_post | Yes | 职位名称,如:高级产品经理、AI算法工程师 | |
| jd | Yes | 职位描述(JD),包括岗位职责、任职要求等 | |
| city_names | Yes | 工作城市列表,如:["上海"] 或 ["北京", "上海"] | |
| salary_min | No | 最低月薪(人民币),如:30000 | |
| salary_max | No | 最高月薪(人民币),如:50000 | |
| experience_min | No | 最低工作年限(年),如:3 | |
| experience_max | No | 最高工作年限(年),如:5 | |
| degree_name | No | 学历要求 | |
| job_nature | No | 工作性质,如:全职、兼职、实习 | |
| job_tags | No | 职位标签,如:["AI", "远程", "股权激励"] | |
| profession_id | No | 职位分类ID(WonderCV 内部编码) | |
| require_ai_fluency | No | 是否优先考虑AI Agent熟练的候选人 |
Implementation Reference
- src/tools/post_job.ts:96-209 (handler)The main execute function for the post_job tool. Validates session, checks quota, calls backend API to post the job, increments usage counter, and returns success/error response with job details.
async execute(input: Input) { // Validate session const session = getSession(input.session_id); if (!session) { return { content: [ { type: 'text', text: JSON.stringify({ error: { code: 'INVALID_SESSION', message: '会话已过期或无效,请重新调用 register_company', }, }, null, 2), }, ], isError: true, }; } // Check quota (preview only - don't consume yet) // TODO: Fix race condition - concurrent requests can both pass this check // See KnownIssues.md #1 - needs reservation/commit pattern or backend metering const remainingBefore = getRemainingQuota(session, 'jobs_posted'); if (remainingBefore <= 0) { return { content: [ { type: 'text', text: JSON.stringify({ error: { code: 'QUOTA_EXCEEDED', message: `今日职位发布额度已用完`, }, access: { tier: session.tier, reset_at: '次日零点', }, }, null, 2), }, ], isError: true, }; } try { const response = await postJob({ session_id: input.session_id, job_post: input.job_post, jd: input.jd, city_names: input.city_names, salary_min: input.salary_min, salary_max: input.salary_max, experience_min: input.experience_min, experience_max: input.experience_max, degree_name: input.degree_name, job_nature: input.job_nature, job_tags: input.job_tags, profession_id: input.profession_id, require_ai_fluency: input.require_ai_fluency, }); const { data } = response; // Consume quota only after successful API call checkAndIncrementUsage(session, 'jobs_posted'); return { content: [ { type: 'text', text: JSON.stringify({ data: { job_token: data.job_token, job_post: data.job_post, status: data.status, status_text: '已发布', expires_at: data.expires_at, tip: data.tip, }, access: { tier: session.tier, jobs_remaining_today: getRemainingQuota(session, 'jobs_posted'), }, meta: { session_id: session.session_id, }, next_steps: [ `调用 search_candidates 为 "${data.job_post}" 搜索匹配候选人`, `调用 list_jobs 查看所有已发布的职位`, ], }, null, 2), }, ], isError: false, }; } catch (error) { const errorMessage = error instanceof Error ? error.message : '发布失败'; return { content: [ { type: 'text', text: JSON.stringify({ error: { code: 'POST_JOB_FAILED', message: errorMessage, }, }, null, 2), }, ], isError: true, }; } }, - src/tools/post_job.ts:13-76 (schema)Zod input schema defining all parameters for posting a job: session_id, job_post, jd, city_names, salary range, experience range, degree, job_nature, job_tags, profession_id, and require_ai_fluency. Includes validation rules and descriptions.
const inputSchema = z.object({ session_id: z.string() .describe('会话 ID,从 register_company 获取'), job_post: z.string() .min(2, '职位名称至少需要2个字符') .max(100, '职位名称不能超过100个字符') .describe('职位名称,如:高级产品经理、AI算法工程师'), jd: z.string() .min(50, '职位描述至少需要50个字符') .max(10000, '职位描述不能超过10000个字符') .describe('职位描述(JD),包括岗位职责、任职要求等'), city_names: z.array(z.string()) .min(1, '请至少填写一个城市') .describe('工作城市列表,如:["上海"] 或 ["北京", "上海"]'), salary_min: z.number() .int() .min(1000) .optional() .describe('最低月薪(人民币),如:30000'), salary_max: z.number() .int() .min(1000) .optional() .describe('最高月薪(人民币),如:50000'), experience_min: z.number() .min(0) .max(50) .optional() .describe('最低工作年限(年),如:3'), experience_max: z.number() .min(0) .max(50) .optional() .describe('最高工作年限(年),如:5'), degree_name: z.enum(['大专', '本科', '硕士', '博士']) .optional() .describe('学历要求'), job_nature: z.string() .optional() .describe('工作性质,如:全职、兼职、实习'), job_tags: z.array(z.string()) .optional() .describe('职位标签,如:["AI", "远程", "股权激励"]'), profession_id: z.number() .int() .positive() .optional() .describe('职位分类ID(WonderCV 内部编码)'), require_ai_fluency: z.boolean() .optional() .describe('是否优先考虑AI Agent熟练的候选人'), }); - src/tools/index.ts:29-57 (registration)Tool registration: imports postJobTool from post_job.js, exports it, and adds it to the allTools registry array which is used to initialize the MCP server.
import { postJobTool } from './post_job.js'; import { listJobsTool } from './list_jobs.js'; import { searchCandidatesTool } from './search_candidates.js'; import { viewCandidateTool } from './view_candidate.js'; import { listApplicationsTool } from './list_applications.js'; import { requestOutreachTool } from './request_outreach.js'; // Export all tools export { registerCompanyTool, postJobTool, listJobsTool, searchCandidatesTool, viewCandidateTool, listApplicationsTool, requestOutreachTool, }; // Tool registry for server initialization // eslint-disable-next-line @typescript-eslint/no-explicit-any export const allTools: Tool<any>[] = [ registerCompanyTool, postJobTool, listJobsTool, searchCandidatesTool, viewCandidateTool, listApplicationsTool, requestOutreachTool, ]; - src/backend-api.ts:207-226 (helper)Backend API helper function that posts the job to the WonderCV API. Converts experience from years to days (WonderCV convention) and makes the HTTP POST request to /jobs endpoint.
export async function postJob( input: PostJobInput ): Promise<ApiResponse<PostJobOutput>> { // Convert experience from years to days for WonderCV backend const body: Record<string, unknown> = { ...input, }; if (input.experience_min !== undefined) { body.experience_min = yearsToDays(input.experience_min); } if (input.experience_max !== undefined) { body.experience_max = yearsToDays(input.experience_max); } return apiRequest('/jobs', { method: 'POST', body, }); } - src/backend-api.ts:176-198 (schema)TypeScript interfaces defining the input and output types for the backend API postJob function. PostJobInput matches the tool input, PostJobOutput contains job_token, job_post, status, expires_at, and tip.
export interface PostJobInput { session_id: string; job_post: string; jd: string; city_names: string[]; salary_min?: number; salary_max?: number; experience_min?: number; // Years - will be converted to days experience_max?: number; // Years - will be converted to days degree_name?: string; job_nature?: string; job_tags?: string[]; profession_id?: number; require_ai_fluency?: boolean; } export interface PostJobOutput { job_token: string; job_post: string; status: number; expires_at: string; tip: string; }