MCP
A remote MCP server so Claude, Cursor, and other MCP clients can create and manage courses as tools — the same operations as the REST API, callable from a conversation.
Connect
Point any MCP client that speaks Streamable HTTP at this URL, with your API key as a Bearer token — the same key from your workspace's API Keys page. Every tool call is scoped to that key's workspace, exactly like the REST API.
{
"underlayer": {
"url": "https://underlayer.outworx.io/api/mcp",
"headers": {
"Authorization": "Bearer sk_live_..."
}
}
}For stdio-only clients that can't reach a remote URL directly, bridge it with mcp-remote:
{
"underlayer": {
"command": "npx",
"args": ["-y", "mcp-remote", "https://underlayer.outworx.io/api/mcp",
"--header", "Authorization:Bearer sk_live_..."]
}
}OAuth, for Claude.ai and other web clients
Claude.ai's browser-based “Add custom connector” flow can't paste in a Bearer header — it authenticates with OAuth instead. Underlayer is its own authorization server for this: add https://underlayer.outworx.io/api/mcp as a custom connector and Claude discovers everything else automatically (RFC 9728 protected-resource metadata → RFC 8414 authorization-server metadata → an authorization-code + PKCE flow, using CIMD to identify itself — no manual client registration on your end).
You'll land on a sign-in screen (if you're not already signed in) and then a consent screen naming the connecting app and your workspace. Approving it grants the same access an API key would — full create/read/update/delete on everything below. Revoke it anytime from Connected Apps in your dashboard; access stops immediately.
One thing that access does not include: minting API keys. A key is a separate credential rather than a view onto this connection, so one created through a connector would keep working after you revoked the connector — access outliving its own revocation. Keys are issued only from the API Keys page, where a person is looking at the screen. Listing and revoking are available as tools, since neither creates access.
Tools
Every REST resource is exposed as a matching set of tools — same validation, same plan gates, same ownership checks. Results come back as JSON.
Authoring tools write; the progress tools only read. That asymmetry is deliberate: a completion is a record of something a real person did, and a tool that could edit one would be a tool that could award a pass.
list_coursesget_coursecreate_courseupdate_coursedelete_courselist_block_typeslist_screen_templatesadd_screenupdate_screenadd_blockduplicate_screendelete_screenreorder_screensgenerate_courseget_generationexport_scormimport_scormlist_themesget_themecreate_themeupdate_themedelete_themelist_collectionsget_collectioncreate_collectionupdate_collectiondelete_collectionadd_course_to_collectionmove_course_in_collectionremove_course_from_collectionlist_translationsget_translationcreate_translationupdate_translationdelete_translationlist_webhooksget_webhookcreate_webhookupdate_webhookdelete_webhooklist_certificatesget_certificatecreate_certificateupdate_certificatedelete_certificatelist_identitiesget_identityupsert_identitybulk_upsert_identitiesdelete_identitybulk_delete_identitieslist_completionsget_completionlist_issued_certificatesget_issued_certificateget_overviewsearch_contentget_usagelist_audit_eventslist_membersinvite_membercancel_inviteupdate_member_roleremove_memberlist_api_keysrevoke_api_keyget_workspace_infoupdate_workspace_settingslist_completions takes identityExternalId, so you can ask about a learner by your own id for them without looking ours up first, and get_issued_certificate accepts a serial however it was typed — case and dashes are normalised, because the serial usually arrives read off a piece of paper.
Prefer the screen tools over update_course for editing content. update_course can only write the whole screens array, so changing one screen of thirty means reading them all back and rewriting the lot — and two edits in flight lose one of them. update_screen does the read-modify-write server-side, on one screen. Start from list_block_types: a block with the wrong field names saves cleanly and then renders empty.
Auth and errors
Every request re-authenticates with the same Bearer key as the REST API — a missing or revoked key fails the connection outright. A tool-level problem (not found, invalid input, plan required) comes back as a normal tool result with isError: true and a human-readable message, not a broken connection.
Actions that change who can get in — inviting, removing, changing a role, revoking a key, renaming the workspace — are written to the audit log naming the key that made them, so an automated change is as traceable as a human one. Read it back with list_audit_events or from Settings in the dashboard.