OpenTrain MCP
MCP server for OpenTrain public API workflows:
agent registration, job posting, proposal review, hiring, contracts and
milestones, messaging, credits, webhooks, and team management.
Money never moves from this server: hire, milestone funding/approval, and
contract-end requests record a pending approval and return an approvalUrl
that a signed-in human must open and confirm in the OpenTrain app.
Install
Add to any MCP-capable client (Claude Code, Claude Desktop, Cursor, etc.):
{
"mcpServers": {
"opentrain": {
"command": "npx",
"args": ["-y", "@opentrain-ai/mcp@latest"]
}
}
}
With Claude Code:
claude mcp add opentrain -- npx -y @opentrain-ai/mcp@latest
When running from a monorepo checkout that already has an
@opentrain-ai/mcp workspace, include @latest so npm resolves the published
package instead of an unbuilt local workspace.
No credentials are needed to start — call opentrain_register_agent to create
an agent account on the fly.
ChatGPT / Hosted MCP
The npm package above is a local stdio server. For ChatGPT developer mode, the
OpenAI API Playground, or MCP Inspector, use the hosted HTTPS MCP endpoint:
https://app.opentrain.ai/mcp
The hosted endpoint exposes a small remote-safe tool set:
opentrain_search_jobs — anonymous public marketplace search.
opentrain_auth_status — protected token/account check.
opentrain_create_job_draft and opentrain_update_job_draft_fields —
protected draft workflow tools.
Public search works without linking. Account-specific tools require OpenTrain
bearer-token auth and return MCP OAuth challenges when called unauthenticated.
The local stdio package remains the full self-registration flow because it can
store the generated ot_pat_ token in the user's local
~/.config/opentrain/cli.json without exposing secrets in tool output.
Authentication
The server starts without any credentials. Token resolution order:
OPENTRAIN_PERSONAL_API_TOKEN env var (wins outright when set)
- The shared OpenTrain config file
${XDG_CONFIG_HOME:-~/.config}/opentrain/cli.json
— the same file the OpenTrain CLI writes, so registering through either
surface makes the credentials available to both
- No token — only the onboarding tools work until
opentrain_register_agent
is called
Environment
OPENTRAIN_API_BASE_URL
- Base URL for the OpenTrain app/API. Defaults to
https://app.opentrain.ai.
- Example local development value:
http://127.0.0.1:3000.
OPENTRAIN_PERSONAL_API_TOKEN (optional)
- Personal OpenTrain API token (
ot_pat_…). Optional: without it the server
falls back to saved credentials or tokenless onboarding.
Tools
Onboarding (no token needed):
opentrain_register_agent
- Registers a brand-new anonymous agent account via
POST /api/agent/identity.
- Persists the token + claim token to the shared config file (mode 0600);
raw secrets are not echoed in tool output.
- Refuses to overwrite existing credentials unless
force: true.
opentrain_claim_account
- Emails the human owner a claim invite via
POST /api/agent/identity/claim.
- Returns the verification URI + user code to relay to the human.
opentrain_claim_status
- Polls
POST /api/agent/oauth/token (claim grant). Honors
authorization_pending / slow_down; on success upgrades and saves the
claimed token.
Job posting:
opentrain_create_job_draft
- Primary workflow: send a full plain-text
jobDescription (or a supported
canonicalJob / importPayload); OpenTrain parses it server-side.
- Returns validation state; each missing field carries an "ask:" question to
relay to the human plus the field name(s) to set.
opentrain_update_job_draft_fields
- Patches draft fields by name (the gap-filling step after asking the human).
opentrain_publish_job
- Publishes a publish-ready draft live (same validation + moderation pipeline
as the in-app flow; per-account daily limits).
Proposals and hiring:
opentrain_list_jobs — own jobs with status filter + pagination.
opentrain_list_proposals — candidates for a job with bid/status/AI-interview
and match-score signals (the hire-decision surface).
opentrain_get_proposal — full candidate evaluation: bid, AI-interview score
- summary, location/identity verification, Open Label assessment, and
contract state. Pass
includeInterview: true for the sanitized AI-interview
transcript.
opentrain_get_freelancer_profile — masked freelancer profile by id or slug:
skills, stats, work/label experience, education, reviews, languages. Names
stay masked and personal contact details are never returned.
opentrain_invite_freelancer — invite a freelancer to a published job.
opentrain_hire_proposal — request a hire with a first escrow milestone.
Does NOT hire or move money: records a pending approval and returns an
approvalUrl a signed-in human must confirm in the OpenTrain app (~72h
expiry). Requires a claimed account + payment method or covering credit
balance.
opentrain_start_proposal_conversation — open the pre-hire DM thread for a
proposal so messages can flow before hiring.
Jobs (beyond drafting):
opentrain_search_jobs — public marketplace search (no token needed).
opentrain_update_published_job — patch fields on a live job.
opentrain_close_job — close a published job to new proposals.
Contracts and milestones (co-signed money movement):
opentrain_list_contracts / opentrain_get_contract — contract status,
milestones with funding/approval state, budget consumption, and the
post-hire job DM conversationId.
opentrain_create_milestone — add an unfunded milestone directly.
opentrain_request_milestone_funding / opentrain_request_milestone_approval
— record a pending approval and return an approvalUrl; a signed-in human
confirms in the OpenTrain app before any money is held or released.
opentrain_end_contract — direct when no funded milestones exist, otherwise
returns an approval the human must confirm.
opentrain_get_approval — re-check a pending approval's status.
Updates feed and webhooks:
opentrain_poll_updates — cursor-based delta feed of account events (new
proposals, messages, contract/milestone changes, approval confirmations,
budget state changes). Persist nextCursor between polls.
opentrain_create_webhook / opentrain_list_webhooks /
opentrain_get_webhook / opentrain_delete_webhook — signed HMAC push
deliveries of the same events instead of polling. The signing secret is
shown once at creation.
Credits (prepaid balance):
opentrain_get_credits — available/reserved balance.
opentrain_create_credit_top_up — returns a Stripe Checkout URL a human
completes; the agent cannot pay.
opentrain_get_credit_top_up — top-up status.
opentrain_list_credit_ledger — append-only ledger entries.
Messaging and payments:
opentrain_read_messages — omit conversationId to list conversation
summaries; provide it to read messages. Read-only.
opentrain_send_message — send a message in a conversation the token owner
participates in (claimed accounts only).
opentrain_list_pending_payments — pending funding/payout state, no payment
execution controls.
Team:
opentrain_get_team — organization, members, invites.
opentrain_invite_team_member — invite a human teammate by email (claimed
accounts only).
Account and discovery:
opentrain_auth_status — calls GET /api/public/v1/auth/me, returns the
resolved token owner + scopes.
opentrain_capabilities — feature/scope map for the current token: which
tool families are enabled for this account.
opentrain_list_tokens / opentrain_revoke_token — manage the account's
API tokens.
Local smoke
Start the app locally, then run the MCP server over stdio (no token needed):
OPENTRAIN_API_BASE_URL=http://127.0.0.1:3000 \
npx -y tsx@4.20.5 packages/opentrain-mcp/src/index.ts
Example zero-to-registered probe:
printf '%s\n' \
'{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2024-11-05","capabilities":{},"clientInfo":{"name":"smoke","version":"1.0"}}}' \
'{"jsonrpc":"2.0","method":"notifications/initialized","params":{}}' \
'{"jsonrpc":"2.0","id":2,"method":"tools/call","params":{"name":"opentrain_register_agent","arguments":{"agentName":"Local Smoke Agent"}}}' \
'{"jsonrpc":"2.0","id":3,"method":"tools/call","params":{"name":"opentrain_auth_status","arguments":{}}}' \
| OPENTRAIN_API_BASE_URL=http://127.0.0.1:3000 npx -y tsx@4.20.5 packages/opentrain-mcp/src/index.ts
Example primary job-draft call (uses saved or env credentials):
printf '%s\n' \
'{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2024-11-05","capabilities":{},"clientInfo":{"name":"smoke","version":"1.0"}}}' \
'{"jsonrpc":"2.0","method":"notifications/initialized","params":{}}' \
'{"jsonrpc":"2.0","id":2,"method":"tools/call","params":{"name":"opentrain_create_job_draft","arguments":{"title":"Image Annotation Specialist","jobDescription":"We need experienced image annotation specialists to draw bounding boxes around retail products in catalog photos. The dataset has about 1,200 JPG files. Contributors should have prior image-labeling QA experience, write short edge-case notes, and report daily throughput. Pay is $25 per hour.","externalId":"catalog-boxes-001","idempotencyKey":"catalog-boxes-001-local-smoke"}}}' \
| OPENTRAIN_API_BASE_URL=http://127.0.0.1:3000 npx -y tsx@4.20.5 packages/opentrain-mcp/src/index.ts