create_bounty_draft
Create an unfunded draft bounty to specify task details, title, amount, and deadline, then fund it separately via Stripe checkout.
Instructions
Create a new bounty as an unfunded DRAFT. Returns task_id and slug. Bounty is created as DRAFT/UNFUNDED. Call fund_bounty next to get a Stripe Checkout URL the user can open to fund. Requires TASKBOUNTY_API_KEY.
Input Schema
| Name | Required | Description | Default |
|---|---|---|---|
| title | Yes | Bounty title (5-200 chars). | |
| short_summary | Yes | One-line summary (10-500 chars). | |
| description | Yes | Full bounty description (20-10000 chars). | |
| category | Yes | Category, e.g. 'code', 'research', 'design'. | |
| bounty_amount | Yes | Bounty amount in USD. | |
| submission_deadline | Yes | ISO 8601 deadline. Must be at least 7 days from now. | |
| evaluation_criteria | No | Optional evaluation criteria. | |
| expected_output_format | No | Optional expected output format. | |
| github_repo_url | No | Optional GitHub repo URL for code tasks. | |
| tags | No | Optional comma-separated tags. | |
| platform | No | Optional platform: 'general' or 'code'. | |
| language | No | Optional language filter (e.g. 'typescript'). |
Implementation Reference
- src/index.ts:349-378 (handler)Handler for the 'create_bounty_draft' tool. Validates required fields (title, short_summary, description, category, bounty_amount, submission_deadline), then POSTs the payload to /api/v1/tasks to create an unfunded draft bounty. Optional fields (evaluation_criteria, expected_output_format, github_repo_url, tags, platform, language) are conditionally added to the body.
case "create_bounty_draft": { const required = ["title", "short_summary", "description", "category", "bounty_amount", "submission_deadline"]; for (const key of required) { if (a[key] === undefined || a[key] === null || a[key] === "") { return { content: [{ type: "text", text: `${key} is required` }], isError: true, }; } } const body: Record<string, unknown> = { title: a.title, short_summary: a.short_summary, description: a.description, category: a.category, bounty_amount: a.bounty_amount, submission_deadline: a.submission_deadline, }; if (typeof a.evaluation_criteria === "string") body.evaluation_criteria = a.evaluation_criteria; if (typeof a.expected_output_format === "string") body.expected_output_format = a.expected_output_format; if (typeof a.github_repo_url === "string") body.github_repo_url = a.github_repo_url; if (typeof a.tags === "string") body.tags = a.tags; if (typeof a.platform === "string") body.platform = a.platform; if (typeof a.language === "string") body.language = a.language; return await tbFetch(`/tasks`, { method: "POST", body: JSON.stringify(body), requireAuth: true, }); } - src/index.ts:170-202 (schema)Input schema definition for the 'create_bounty_draft' tool. Defines all properties (title, short_summary, description, category, bounty_amount, submission_deadline as required; evaluation_criteria, expected_output_format, github_repo_url, tags, platform, language as optional) and their types.
{ name: "create_bounty_draft", description: "Create a new bounty as an unfunded DRAFT. Returns task_id and slug. Bounty is created as DRAFT/UNFUNDED. Call fund_bounty next to get a Stripe Checkout URL the user can open to fund. Requires TASKBOUNTY_API_KEY.", inputSchema: { type: "object", properties: { title: { type: "string", description: "Bounty title (5-200 chars)." }, short_summary: { type: "string", description: "One-line summary (10-500 chars)." }, description: { type: "string", description: "Full bounty description (20-10000 chars)." }, category: { type: "string", description: "Category, e.g. 'code', 'research', 'design'." }, bounty_amount: { type: "number", description: "Bounty amount in USD." }, submission_deadline: { type: "string", description: "ISO 8601 deadline. Must be at least 7 days from now.", }, evaluation_criteria: { type: "string", description: "Optional evaluation criteria." }, expected_output_format: { type: "string", description: "Optional expected output format." }, github_repo_url: { type: "string", description: "Optional GitHub repo URL for code tasks." }, tags: { type: "string", description: "Optional comma-separated tags." }, platform: { type: "string", description: "Optional platform: 'general' or 'code'." }, language: { type: "string", description: "Optional language filter (e.g. 'typescript')." }, }, required: [ "title", "short_summary", "description", "category", "bounty_amount", "submission_deadline", ], }, }, - src/index.ts:270-277 (registration)The 'create_bounty_draft' tool is registered in the TOOLS array (line 79) which is exposed via the ListToolsRequestSchema handler (line 275-277). The CallToolRequestSchema handler (line 279) dispatches to the case statement on line 349.
const server = new Server( { name: "taskbounty-mcp-server", version: "0.1.0" }, { capabilities: { tools: {} } }, ); server.setRequestHandler(ListToolsRequestSchema, async () => ({ tools: TOOLS as unknown as typeof TOOLS, })); - src/index.ts:23-77 (helper)The tbFetch helper function is used by the handler to make the POST request to /api/v1/tasks. It handles authentication via Bearer token, JSON content-type, and error responses.
async function tbFetch( path: string, init: RequestInit & { requireAuth?: boolean } = {}, ): Promise<ToolResult> { const { requireAuth, headers, ...rest } = init; if (requireAuth && !API_KEY) { return { content: [ { type: "text", text: "Missing TASKBOUNTY_API_KEY environment variable. Set it to your tb_live_* key from https://www.task-bounty.com/dashboard/api-keys.", }, ], isError: true, }; } const url = `${API_BASE}${path}`; const finalHeaders: Record<string, string> = { Accept: "application/json", ...(headers as Record<string, string> | undefined), }; if (API_KEY) finalHeaders["Authorization"] = `Bearer ${API_KEY}`; if (rest.body && !finalHeaders["Content-Type"]) { finalHeaders["Content-Type"] = "application/json"; } let res: Response; try { res = await fetch(url, { ...rest, headers: finalHeaders }); } catch (err) { return { content: [ { type: "text", text: `Network error calling ${url}: ${err instanceof Error ? err.message : String(err)}`, }, ], isError: true, }; } const text = await res.text(); if (!res.ok) { return { content: [ { type: "text", text: `HTTP ${res.status} ${res.statusText} from ${url}\n\n${text}`, }, ], isError: true, }; } return { content: [{ type: "text", text }] }; }