@getabrain/mcp-server
Advanced tools
Sorry, the diff of this file is not supported yet
+85
-9
@@ -34,3 +34,8 @@ #!/usr/bin/env node | ||
| function ok(data) { | ||
| return { content: [{ type: "text", text: typeof data === "string" ? data : JSON.stringify(data) }] }; | ||
| const text = typeof data === "string" ? data : JSON.stringify(data); | ||
| const res = { content: [{ type: "text", text }] }; | ||
| if (data !== null && typeof data === "object" && !Array.isArray(data)) { | ||
| res.structuredContent = data; | ||
| } | ||
| return res; | ||
| } | ||
@@ -62,2 +67,11 @@ function fail(message) { | ||
| inputShape: {}, | ||
| annotations: { title: "Check prepaid balance", readOnlyHint: true, openWorldHint: true }, | ||
| outputShape: { | ||
| balance_cents: z.number().optional(), | ||
| company_name: z.string().optional(), | ||
| mode: z.string().optional(), | ||
| auto_reload_enabled: z.boolean().optional(), | ||
| auto_reload_setup_url: z.string().optional(), | ||
| hint: z.string().optional() | ||
| }, | ||
| run: async (_args, client) => { | ||
@@ -83,2 +97,10 @@ const b = await client.account.balance(); | ||
| }, | ||
| annotations: { | ||
| title: "Create top-up payment link", | ||
| readOnlyHint: false, | ||
| destructiveHint: false, | ||
| idempotentHint: false, | ||
| openWorldHint: true | ||
| }, | ||
| outputShape: { checkout_url: z.string().optional() }, | ||
| run: async (args, client) => ok(await client.account.createTopupLink(args.amount_cents)) | ||
@@ -97,3 +119,3 @@ }, | ||
| bid_amount_cents: z.number().int().min(5).max(1e7).describe( | ||
| "Cents paid to EACH worker per accepted response (min 5 = $0.05, max 10000000 = $100,000.00). Total cost = (bid_amount_cents + bonus_amount_cents) * required_responses, deducted from balance in live mode. No charge occurs in test mode." | ||
| "Cents paid to EACH worker per accepted response (absolute floor 5 = $0.05, max 10000000 = $100,000.00). The REAL minimum is effort-based, not flat: it scales with how long the query type honestly takes a worker to answer, priced at a fair ~$9/hr (0.25 cents/second). Quick types (yes_no, multiple_choice, sentiment, ab_test, headline_test, rating_scale) floor around 5 cents; medium types (image_comparison, image_selection, ranking, text, image_analysis) around 8-12 cents; capture types (voice_capture, photo_capture, custom) around 15 cents; longer types (free_form_text, video_capture) around 30 cents. video_review and audio_review scale with the actual clip length (content_data.video_duration_seconds / audio_duration_seconds) plus a review/write-up overhead, so a 10-minute video review requires roughly $1.65+ -- there is no upper cap, longer clips need proportionally higher bids. Bidding below the type-appropriate minimum is rejected with a 400 telling you the exact floor; call GET /requestor/suggested-bid?type=... for the current min_bid_cents and suggested_bid_cents for a given type before submitting. Total cost = (bid_amount_cents + bonus_amount_cents) * required_responses, deducted from balance in live mode. No charge occurs in test mode." | ||
| ), | ||
@@ -104,2 +126,17 @@ description: z.string().optional().describe("Optional longer explanation/context shown to workers alongside the title, for extra instructions or background."), | ||
| }, | ||
| annotations: { | ||
| title: "Ask human workers a question", | ||
| readOnlyHint: false, | ||
| destructiveHint: false, | ||
| idempotentHint: false, | ||
| openWorldHint: true | ||
| }, | ||
| outputShape: { | ||
| query_id: z.string().optional(), | ||
| id: z.string().optional(), | ||
| status: z.string().optional(), | ||
| required_responses: z.number().optional(), | ||
| total_cost_cents: z.number().optional(), | ||
| simulated: z.boolean().optional() | ||
| }, | ||
| run: async (args, client) => ok(await client.queries.create(args)) | ||
@@ -113,2 +150,9 @@ }, | ||
| }, | ||
| annotations: { title: "Get query responses", readOnlyHint: true, openWorldHint: true }, | ||
| outputShape: { | ||
| status: z.string().optional(), | ||
| completed_responses: z.number().optional(), | ||
| required_responses: z.number().optional(), | ||
| responses: z.array(z.any()).optional() | ||
| }, | ||
| run: async (args, client) => { | ||
@@ -132,2 +176,10 @@ const q = await client.queries.get(args.query_id); | ||
| }, | ||
| annotations: { title: "Wait for human responses", readOnlyHint: true, openWorldHint: true }, | ||
| outputShape: { | ||
| status: z.string().optional(), | ||
| responses: z.array(z.any()).optional(), | ||
| completed_responses: z.number().optional(), | ||
| required_responses: z.number().optional(), | ||
| hint: z.string().optional() | ||
| }, | ||
| run: async (args, client) => { | ||
@@ -165,2 +217,10 @@ const current = await client.queries.get(args.query_id); | ||
| }, | ||
| annotations: { title: "List your queries", readOnlyHint: true, openWorldHint: true }, | ||
| outputShape: { | ||
| queries: z.array(z.any()).optional(), | ||
| total: z.number().optional(), | ||
| limit: z.number().optional(), | ||
| offset: z.number().optional(), | ||
| has_more: z.boolean().optional() | ||
| }, | ||
| run: async (args, client) => ok(await client.queries.list(args)) | ||
@@ -177,2 +237,14 @@ }, | ||
| }, | ||
| annotations: { | ||
| title: "Rate a worker response", | ||
| readOnlyHint: false, | ||
| destructiveHint: false, | ||
| idempotentHint: false, | ||
| openWorldHint: true | ||
| }, | ||
| outputShape: { | ||
| worker_quality_score: z.number().optional(), | ||
| success: z.boolean().optional(), | ||
| message: z.string().optional() | ||
| }, | ||
| run: async (args, client) => ok(await client.responses.rate(args.query_id, args.response_id, { score: args.score, feedback_text: args.feedback_text })) | ||
@@ -184,11 +256,15 @@ } | ||
| function createServer(client) { | ||
| const server = new McpServer({ name: "getabrain", version: "0.1.0" }); | ||
| const server = new McpServer({ name: "getabrain", version: "0.2.1" }); | ||
| for (const t of tools) { | ||
| server.tool(t.name, t.description, t.inputShape, async (args) => { | ||
| try { | ||
| return await t.run(args, client); | ||
| } catch (e) { | ||
| return toToolError(e); | ||
| server.registerTool( | ||
| t.name, | ||
| { description: t.description, inputSchema: t.inputShape, outputSchema: t.outputShape, annotations: t.annotations }, | ||
| async (args) => { | ||
| try { | ||
| return await t.run(args, client); | ||
| } catch (e) { | ||
| return toToolError(e); | ||
| } | ||
| } | ||
| }); | ||
| ); | ||
| } | ||
@@ -195,0 +271,0 @@ return server; |
+7
-4
| { | ||
| "name": "@getabrain/mcp-server", | ||
| "version": "0.2.0", | ||
| "version": "0.2.1", | ||
| "description": "MCP server for GetABrain.ai \u2014 real human judgment as agent tools", | ||
@@ -12,6 +12,7 @@ "type": "module", | ||
| "dist", | ||
| "README.md" | ||
| "README.md", | ||
| "logo.png" | ||
| ], | ||
| "scripts": { | ||
| "build": "tsup src/index.ts --format esm --dts --clean --tsconfig tsconfig.build.json", | ||
| "build": "tsup src/index.ts --format esm --clean --tsconfig tsconfig.build.json", | ||
| "prepublishOnly": "npm run build" | ||
@@ -28,3 +29,5 @@ }, | ||
| "devDependencies": { | ||
| "tsup": "^8.0.0" | ||
| "@types/node": "^22", | ||
| "tsup": "^8.0.0", | ||
| "typescript": "^5" | ||
| }, | ||
@@ -31,0 +34,0 @@ "license": "MIT", |
+27
-0
@@ -0,1 +1,5 @@ | ||
| <p align="center"> | ||
| <img src="./logo.png" alt="GetABrain" width="128" height="128" /> | ||
| </p> | ||
| # @getabrain/mcp-server | ||
@@ -26,2 +30,25 @@ | ||
| ## Remote (hosted) MCP server -- no install | ||
| Prefer not to run anything locally? GetABrain also hosts this same server over Streamable HTTP at | ||
| `https://www.getabrain.ai/api/mcp`. Point any MCP client that supports remote servers at that URL and | ||
| pass your key pair as headers instead of env vars: | ||
| ```json | ||
| { | ||
| "mcpServers": { | ||
| "getabrain": { | ||
| "url": "https://www.getabrain.ai/api/mcp", | ||
| "headers": { | ||
| "X-API-Key": "gab_k_…", | ||
| "X-API-Secret": "gab_s_…" | ||
| } | ||
| } | ||
| } | ||
| } | ||
| ``` | ||
| Same 7 tools, same schemas, same test-mode support -- see `docs/deploy/remote-mcp.md` in this repo for | ||
| details (Smithery-style clients, auth requirements, etc). | ||
| ## Test mode | ||
@@ -28,0 +55,0 @@ |
| #!/usr/bin/env node |
Long strings
Supply chain riskContains long string literals, which may be a sign of obfuscated or packed code.
248862
1260.2%283
38.73%99
37.5%3
200%3
50%