MCP
Verified request, response and authorization contracts.
The resource origin is configured by MELSO_MCP_PUBLIC_URL (mcpURLFromEnv in router.go); the router itself has no hosted-origin default. /api/mcp and /mcp are mounted. Discover the actual resource from /.well-known/oauth-protected-resource. MCP uses JSON-RPC over HTTP, not a REST endpoint per tool.
Supported actors are an active Run token or an OAuth grant. A PAT alone is rejected by HandleMCP. An OAuth grant is either pinned to one company or, when the person allows all their companies at consent, user-scoped: it has no fixed company, workspaces_list returns the companies it reaches, and every other tool takes an optional workspace argument (a company slug or id). With more than one company and no workspace, the call is refused and names the choices. Membership is checked on every call, and a pinned grant refuses a workspace naming another company. Upload routes take the same selector as ?workspace=. People see and disconnect their OAuth connections in Settings → MCP (GET /api/oauth/mcp/grants, DELETE /api/oauth/mcp/grants/{grantId}). The tool list depends on the credential profile; ask tools/list and use tools_search for advanced tool schemas, then tools_call for discovered tools. Do not infer available tools from this page alone.
Core capabilities include task create/get/list/update/send/wait, Areas, the company's skills (skills_list, skills_get, skills_search, skills_install, skills_save, skills_update, skills_delete), the company profile (company_get), Run cancellation, memories and tool discovery. Member credentials additionally expose authorized start/execution capabilities; Run credentials cannot start arbitrary work. Every actor gets the Vault tools secrets_list and secrets_request; OAuth grants also get secrets_set, secrets_update and secrets_delete, and upload files to /upload-secret. tasks_wait is a stateless observation lasting at most 20 seconds; it reports needs_input when a Run is waiting on a person. tasks_get then lists the question in pending_interactions (its payload.questions and options), and an OAuth connection can answer or decline a clarification with tasks_interactions_resolve (answers keyed by question id). The recipient-or-admin rule still applies, approvals and app-connection requests are left to a person, and the answer is recorded as given through an MCP client. Durable child waits are separate advanced tools. For external integrations, outbound webhooks avoid repeated waits.
company_get returns the company name, description, website, instructions and research status. company_update accepts name, website_url or instructions for an owner or admin. The instructions reach every Run in the company. areas_list and areas_get include each Area's instructions; areas_create and areas_update can set them. Area instructions reach Runs in that Area. The MCP server does not expose Library files.* tools.
On connect the server identifies itself as melso with a title, description and website, and sends instructions that open by telling the client when to delegate: call tasks_create with the request as input for multi-step or long-running work, or work that needs the company's knowledge or connected apps. Some clients keep only the first 2,048 characters of instructions, so the delegation brief and the create, wait and read loop come first (mcpServerInstructions in handler/mcp.go).
Before tasks_create or tasks_start, call execution_options. For each harness it says who pays for the model (funding: the person's connected AI account, a stored key, or Melso credits), how many AI accounts are connected, the company's credit state, and connect_url where the person connects an account. With no account connected, a client should offer that link before spending credits and keep the spend to one small Run; the melso-execution-options guide at https://melso.ai/skills/melso-execution-options.md covers choosing the harness and model.
Settings → MCP gives a short setup prompt that points an AI client at https://melso.ai/skills/melso-onboarding-mcp.md. That guide walks the client through connecting the remote MCP (or the CLI when it has no MCP), then setting up the company's Areas, skills, repositories and apps from what it already knows.
OAuth uses authorization code + PKCE S256, rotating refresh tokens, and scope mcp:tools. Fetch server metadata for the consent URL rather than constructing it. /api/oauth/mcp/authorize is the signed-in consent backend; it is not a substitute for user consent.
Examples use the environment variables from Overview. UUID placeholders must be replaced with your own IDs.
GET /.well-known/oauth-protected-resource
Auth: Public.
Request: No body.
Response: {resource:string,authorization_servers:string[],bearer_methods_supported:string[],scopes_supported:string[]}.
Status: 200; 503 OAuth URL not configured. Authentication and resource-access errors follow Overview.
curl -sS -X GET "$MELSO_URL/.well-known/oauth-protected-resource" \
-H "Authorization: Bearer $MELSO_TOKEN" -H "X-Workspace-ID: $WORKSPACE_ID"GET /.well-known/oauth-authorization-server
Auth: Public.
Request: No body.
Response: OAuth metadata including issuer, authorization/token/registration/revocation endpoints, grant types, PKCE methods and supported scope.
Status: 200; 503 OAuth URL not configured. Authentication and resource-access errors follow Overview.
curl -sS -X GET "$MELSO_URL/.well-known/oauth-authorization-server" \
-H "Authorization: Bearer $MELSO_TOKEN" -H "X-Workspace-ID: $WORKSPACE_ID"POST /oauth/register
Auth: Public dynamic client registration.
Request: client_name:string (1–200), redirect_uris:string[] (1–10), optional grant_types:string[], token_endpoint_auth_method:"none".
Response: Registered client metadata with client_id; no client secret.
Status: 201; 400 invalid metadata; 429 rate limited. Authentication and resource-access errors follow Overview.
curl -sS -X POST "$MELSO_URL/oauth/register" \
-H "Authorization: Bearer $MELSO_TOKEN" -H "X-Workspace-ID: $WORKSPACE_ID" \
-H "Content-Type: application/json" -d '{"client_name":"Factory","redirect_uris":["http://127.0.0.1:8080/callback"],"token_endpoint_auth_method":"none"}'POST /oauth/token
Auth: OAuth client with valid code/verifier or refresh token.
Request: Form-urlencoded: authorization code grant uses grant_type=authorization_code, client_id, code, redirect_uri, code_verifier, resource as required by the grant. Refresh uses grant_type=refresh_token, client_id, refresh_token.
Response: {access_token:string,refresh_token:string,token_type:"Bearer",expires_in:integer,scope:string}.
Status: 200; 400 invalid grant; 429 rate limited. Authentication and resource-access errors follow Overview.
Form example: curl -sS "$MELSO_URL/oauth/token" --data-urlencode grant_type=refresh_token --data-urlencode "client_id=$CLIENT_ID" --data-urlencode "refresh_token=$REFRESH_TOKEN".
curl -sS "$MELSO_URL/oauth/token" --data-urlencode grant_type=refresh_token --data-urlencode "client_id=$CLIENT_ID" --data-urlencode "refresh_token=$REFRESH_TOKEN"POST /oauth/revoke
Auth: Token holder.
Request: Form-urlencoded token:string.
Response: Empty.
Status: 200; 400 missing token. Authentication and resource-access errors follow Overview.
Form example: curl -sS "$MELSO_URL/oauth/revoke" --data-urlencode "token=$MCP_TOKEN".
curl -sS "$MELSO_URL/oauth/revoke" --data-urlencode "token=$MCP_TOKEN"GET /api/oauth/mcp/authorize
Auth: Signed-in member; validates the OAuth client and requested resource.
Request: OAuth authorization query fields: client_id, redirect_uri, response_type=code, code_challenge, code_challenge_method=S256, optional scope/resource as validated by the handler.
Response: {client_name:string,scope:string}.
Status: 200; 400 invalid consent request. Authentication and resource-access errors follow Overview.
curl -sS -X GET "$MELSO_URL/api/oauth/mcp/authorize" \
-H "Authorization: Bearer $MELSO_TOKEN" -H "X-Workspace-ID: $WORKSPACE_ID"POST /api/oauth/mcp/authorize
Auth: Signed-in member approving access to their selected workspace.
Request: client_id:string, redirect_uri:string, state:string, code_challenge:string, resource:string, scope:string, workspace_id:UUID.
Response: {redirect_uri:string} containing a short-lived authorization code.
Status: 200; 400 invalid consent; 403/404 workspace access denied. Authentication and resource-access errors follow Overview.
curl -sS -X POST "$MELSO_URL/api/oauth/mcp/authorize" \
-H "Authorization: Bearer $MELSO_TOKEN" -H "X-Workspace-ID: $WORKSPACE_ID" \
-H "Content-Type: application/json" -d '{"client_id":"client-id","redirect_uri":"http://127.0.0.1:8080/callback","state":"random-state","code_challenge":"S256-challenge","resource":"https://mcp.melso.ai/mcp","scope":"mcp:tools","workspace_id":"00000000-0000-4000-8000-000000000001"}'POST /api/mcp
Auth: MCP OAuth access token or active Run token; no PAT.
Request: JSON-RPC {jsonrpc:"2.0",id:string|number,method:string,params?:object}. Initialize first. For tools/call, params are {name:string,arguments:object}.
Response: JSON-RPC {jsonrpc:"2.0",id,result} or {jsonrpc:"2.0",id,error}; tool results include content blocks and optional structured data.
Status: 200 JSON-RPC response; 202 notification; 400/403 invalid protocol or actor; 413 oversized request. Authentication and resource-access errors follow Overview.
Replace $MELSO_TOKEN in this example with $MCP_TOKEN. For a legacy 2025 client send Accept: application/json, text/event-stream and its negotiated MCP-Protocol-Version. Modern clients must use their negotiated protocol headers; the server validates protocol era in mcp_protocol.go. The same POST handler is available at /mcp.
curl -sS -X POST "$MELSO_URL/api/mcp" \
-H "Authorization: Bearer $MCP_TOKEN" -H "X-Workspace-ID: $WORKSPACE_ID" \
-H "Content-Type: application/json" -H "Accept: application/json, text/event-stream" -H "MCP-Protocol-Version: 2025-06-18" -d '{"jsonrpc":"2.0","id":1,"method":"tools/list","params":{}}'POST /api/mcp/upload-file
Auth: MCP OAuth token; uses its granted workspace (or ?workspace= for a user-scoped grant). Run credentials retain their Task scope.
Request: Multipart file:binary required; same size limit (under 100 MiB per request on melso.ai and mcp.melso.ai) and optional Task/message/Run association fields as POST /api/upload-file in Tasks. Upload each file separately.
Response: AttachmentResponse (see Tasks). Pass returned IDs as attachment_ids:UUID[] to tasks_create or tasks_send.
Status: 200; 400 invalid upload; 403 denied; 503 storage unavailable. Authentication and resource-access errors follow Overview.
Append /upload-file to the MCP resource URL discovered from metadata. HTML, PDF, image and video files share this request shape. Starting a Task still requires nonempty input.
curl -sS -X POST "$MELSO_URL/api/mcp/upload-file" \
-H "Authorization: Bearer $MCP_TOKEN" -H "X-Workspace-ID: $WORKSPACE_ID" -F "file=@report.pdf"POST /mcp/upload-file
Auth: MCP OAuth token; uses its granted workspace (or ?workspace= for a user-scoped grant). Run credentials retain their Task scope.
Request: Multipart file:binary required; same size limit (under 100 MiB per request on melso.ai and mcp.melso.ai) and optional Task/message/Run association fields as POST /api/upload-file in Tasks. Upload each file separately.
Response: AttachmentResponse (see Tasks). Pass returned IDs as attachment_ids:UUID[] to tasks_create or tasks_send.
Status: 200; 400 invalid upload; 403 denied; 503 storage unavailable. Authentication and resource-access errors follow Overview.
Append /upload-file to the MCP resource URL discovered from metadata. HTML, PDF, image and video files share this request shape. Starting a Task still requires nonempty input.
curl -sS -X POST "$MELSO_URL/mcp/upload-file" \
-H "Authorization: Bearer $MCP_TOKEN" -H "X-Workspace-ID: $WORKSPACE_ID" -F "file=@report.pdf"POST /mcp/upload-skill
Auth: MCP OAuth token or Run credential; uses its workspace (or ?workspace= for a user-scoped grant). Also served at /api/mcp/upload-skill.
Request: Multipart file:binary required: a .zip or .skill archive of one skill folder with its SKILL.md, scripts and binary files. Optional on_conflict: fail (default), overwrite, rename or skip.
Response: {status:"created"|"updated"|"skipped"|"conflict"|"failed",reason?:string,skill?:SkillResponse,existing_skill?:{id,name}}.
Status: 201 created; 200 updated or skipped; 400 not a multipart archive; 403 denied; 409 a skill with that name exists; 413 too large. Authentication and resource-access errors follow Overview.
Use it for a skill folder skills_install cannot fetch, such as one that exists only on your machine. The skill is installed in every Run.
(cd ~/.claude/skills && zip -r /tmp/pdf.zip pdf)
curl -sS -X POST "$MELSO_URL/mcp/upload-skill" \
-H "Authorization: Bearer $MCP_TOKEN" -H "X-Workspace-ID: $WORKSPACE_ID" -F "file=@/tmp/pdf.zip"