Underlayer
Build, publish and track in-product training courses from your AI assistant: courses, learners, completions and SCORM.
Hosted MCP Server
npx add-mcp 'https://underlayerhq.com/api/mcp'Installs into Claude Code, Codex, Cursor and more
Documentation
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.
Claude Desktop / Cursor mcpServers configjson
{
"underlayer": {
"url": "https://underlayerhq.com/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://underlayerhq.com/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://underlayerhq.com/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.
Courses
list_courses get_course create_course update_course delete_course
Screens & blocks
list_block_types list_screen_templates add_screen update_screen add_block duplicate_screen delete_screen reorder_screens
AI generationBUILD PLAN
generate_course get_generation
SCORMSCALE PLAN
export_scorm import_scorm
ThemesSCALE PLAN
list_themes get_theme create_theme update_theme delete_theme
Collections
list_collections get_collection create_collection update_collection delete_collection add_course_to_collection move_course_in_collection remove_course_from_collection
TranslationsSCALE PLAN
list_translations get_translation create_translation update_translation delete_translation
Webhooks
list_webhooks get_webhook create_webhook update_webhook delete_webhook
Certificate templatesSCALE PLAN
list_certificates get_certificate create_certificate update_certificate delete_certificate
Identities
list_identities get_identity upsert_identity bulk_upsert_identities delete_identity bulk_delete_identities
Learner progressREAD-ONLY
list_completions get_completion list_issued_certificates get_issued_certificate
ReportingREAD-ONLY
get_overview search_content get_usage list_audit_events
Team & credentials
list_members invite_member cancel_invite update_member_role remove_member list_api_keys revoke_api_key
Workspace
get_workspace_info update_workspace_settings
list_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.