@thoughtindustries/thoughtindustries-mcp
v0.3.0
Published
MCP server for the [Thought Industries](https://www.thoughtindustries.com/) Enterprise Learning Platform.
Readme
Thought Industries MCP Server
MCP server for the Thought Industries Enterprise Learning Platform.
Covers both APIs — 101 tools total:
- REST (Incoming API v2) — 80 curated tools +
search_actions/execute_actionfor all 135 endpoints - GraphQL (Helium) — 19 tools: auth (Login/Logout), schema discovery, catalog query, learner profile, and all documented Learner + Assessment mutations
Credential models
This server supports two ways to supply credentials, selected by MCP_TRANSPORT:
stdio(default) — single-tenant. Credentials are read once at startup from env vars. Use this for localnpx/docker run -iwith Claude Desktop or Cursor.http— multi-tenant. Run one shared server (e.g. on Google Cloud Run) with no TI credentials on the server. Each user passes their own credentials as HTTP headers on every request, so one deployment serves many users across different TI instances. See Multi-tenant HTTP deployment.
Env vars (stdio / single-tenant)
| Env var | Required | Description |
|---------|----------|-------------|
| TI_BASE_URL | ✅ | Your TI instance, e.g. https://acme.thoughtindustries.com (no trailing slash) |
| TI_API_KEY | ✅ | REST Bearer token and default Helium apiKey — from Settings › Security |
| TI_HELIUM_API_KEY | optional | Separate Helium apiKey if different from TI_API_KEY |
| TI_HELIUM_USER_TOKEN | optional | User "Auth Token" from the Login mutation for user-scoped GraphQL ops |
Request headers (http / multi-tenant)
In http mode the server reads no TI_* env vars — credentials arrive per request:
| Header | Maps to | Required |
|--------|---------|----------|
| X-TI-Base-URL | TI_BASE_URL | ✅ |
| X-TI-Api-Key | TI_API_KEY | ✅ |
| X-TI-Helium-Api-Key | TI_HELIUM_API_KEY | optional (defaults to X-TI-Api-Key) |
| X-TI-Helium-User-Token | TI_HELIUM_USER_TOKEN | optional |
A POST /mcp request missing X-TI-Base-URL or X-TI-Api-Key is rejected with 401.
Curated tools
Connectivity
| Tool | Endpoint | Description |
|------|----------|-------------|
| ping | GET /incoming/v2/ping | Test authentication and connectivity |
Users
| Tool | Endpoint | Description |
|------|----------|-------------|
| list_users | GET /incoming/v2/users | List all learners/admins (filter by email, clientId) |
| get_user | GET /incoming/v2/users/{id} | Get a user's full profile and enrollment details |
| create_user | POST /incoming/v2/users | Create a new user |
| update_user | PUT /incoming/v2/users/{id} | Update a user's profile fields |
| delete_user | DELETE /incoming/v2/users/{id} | Permanently delete a user and all their data |
| update_user_status | PUT /incoming/v2/users/{id}/updateStatus | Enable or disable a user account |
| bulk_import_users | POST /incoming/v2/users/bulkImport | Import users in bulk from a CSV URL (returns job ID) |
| bulk_assign_content | POST /incoming/v2/users/bulkContentAssignment | Enroll multiple users in a course at once |
| bulk_remove_content_access | POST /incoming/v2/users/bulkContentRemoveAccess | Remove course/path access for multiple users |
| reset_user_progress | POST /incoming/v2/users/{id}/resetProgress | Reset all progress for a user on a specific course |
| certify_user | POST /incoming/v2/users/{id}/certify | Grant a certificate to a user for a course or path |
Content & Courses
| Tool | Endpoint | Description |
|------|----------|-------------|
| list_content | GET /incoming/v2/content | Browse the full content catalog |
| create_course | POST /incoming/v2/content/course/create | Create a new course (returns job ID) |
| update_course | PUT /incoming/v2/content/course/update | Update an existing course's metadata |
| list_course_groups | GET /incoming/v2/courseGroups | List all course groups (catalog items) |
| get_course_group | GET /incoming/v2/courseGroups/{id} | Get a course group by UUID |
| get_course_group_by_slug | GET /incoming/v2/courseGroups/slug/{slug} | Get a course group by URL slug |
| list_course_group_courses | GET /incoming/v2/courseGroups/{id}/courses | List all courses/sessions within a course group |
| get_course_structure | GET /incoming/v2/courses/{courseId}/structure | Get sections → lessons → topics tree |
Assessments & Assignments
| Tool | Endpoint | Description |
|------|----------|-------------|
| list_assessment_attempts | GET /incoming/v2/assessmentAttempts | List quiz/assessment attempts |
| update_assessment_grade | PUT /incoming/v2/assessmentAttempts/{id}/updateGrade | Manually override the grade for an assessment attempt |
| list_assignment_submissions | GET /incoming/v2/assignmentSubmissions | List all learner assignment submissions |
Redemption Codes & Coupons
| Tool | Endpoint | Description |
|------|----------|-------------|
| list_redemption_codes | GET /incoming/v2/redemptionCodes | List all redemption code groups |
| create_redemption_code | POST /incoming/v2/redemptionCodes | Create a redemption code group granting content access |
| get_redemption_code | GET /incoming/v2/redemptionCodes/{id} | Get a redemption code group by UUID |
| list_coupons | GET /incoming/v2/coupons | List all coupons |
Ecommerce
| Tool | Endpoint | Description |
|------|----------|-------------|
| list_non_recurring_purchases | GET /incoming/v2/ecommerce/nonRecurringPurchases | List one-time purchases made on the platform |
Meetings
| Tool | Endpoint | Description |
|------|----------|-------------|
| list_meetings | GET /incoming/v2/meetings | List scheduled meetings (webinar/in-person event sessions) |
Clients (Panorama)
| Tool | Endpoint | Description |
|------|----------|-------------|
| list_clients | GET /incoming/v2/clients | List client organizations (Panorama accounts) |
| get_client | GET /incoming/v2/clients/{id} | Get a client organization by UUID |
| create_client | POST /incoming/v2/clients | Create a new client/organization |
| list_client_licenses | GET /incoming/v2/clients/{id}/licenses | List all licenses assigned to a client |
| create_license | POST /incoming/v2/licenses | Create a new license for a client organization |
| get_license | GET /incoming/v2/licenses/{id} | Get a license by UUID |
Discovery
| Tool | Endpoint | Description |
|------|----------|-------------|
| list_tags | GET /incoming/v2/tags (via search) | List all tags defined in the platform |
| get_events_course_action | GET /incoming/v2/events/courseAction | Get enrollment/completion/certificate events |
| get_job | GET /incoming/v2/jobs/{id} | Poll async job status (bulk import, course create, etc.) |
All remaining REST endpoints — additional events, logs, licenses, credits, locations, XLF exports, learning path milestones, and more — are accessible via search_actions + execute_action.
GraphQL tools (Helium API)
Authentication flow
gql_login(email, password) → auth_token string
└─ pass as user_token to all gql_* learner toolsTI uses a non-standard Auth Token header (with a space) for user auth. The server handles this transparently — just pass the token string from gql_login.
apiKey (admin/schema operations) is sent as ?apiKey=… — uses TI_HELIUM_API_KEY, defaulting to TI_API_KEY.
Auth tools
| Tool | Schema | Description |
|------|--------|-------------|
| gql_login | Login(email, password): String! | Authenticate a learner. Returns the Auth Token string to use as user_token. |
| gql_logout | Logout: Boolean! | Invalidate a user's Auth Token. |
Discovery tools
| Tool | Description |
|------|-------------|
| graphql_query | Execute any Helium query or mutation. Full query, variables, user_token support. |
| graphql_introspect | List all root query and mutation field names. Run this first. |
| gql_introspect_type | Inspect a specific type's fields (e.g. gql_introspect_type({ type_name: "User" })). |
Query tools
| Tool | Schema | Description |
|------|--------|-------------|
| gql_catalog_content | CatalogContent(page: Int!, query?, contentTypes?, sortColumn?, sortDirection?): CatalogContent! | Learner-facing content catalog. +3 complexity overhead. |
| gql_current_user | CurrentUser: User | Learner profile — id, email, name, balance, course counts. Requires user token. |
Learner mutation tools (all require a learner-or-manager Auth Token)
| Tool | Schema | Description |
|------|--------|-------------|
| gql_update_course_status | UpdateCourseStatus(id/slug/sku, status, statusChangeDate?): Boolean! | Mark a course started, completed, or not-completed. |
| gql_update_topic_progress | UpdateTopicAndCourseProgress(topicId, progress: 0-100): Boolean! | Update percent complete on a topic (propagates to course). |
| gql_archive_course | ArchiveUserCourse(id): ID | Hide a course from the learner's dashboard. |
| gql_reinstate_course | ReinstateUserCourse(id): ID | Restore an archived course. |
| gql_archive_learning_path | ArchiveUserLearningPath(id): ID | Hide a learning path from the learner's dashboard. |
| gql_reinstate_learning_path | ReinstateUserLearningPath(id): ID | Restore an archived learning path. |
| gql_redeem_balance | RedeemBalance(contentType, contentId): URL! | Redeem credit balance for a course. |
| gql_redeem_code | RedeemRegistrationAndRedemptionCodes(codes): { redeemed } | Redeem registration or redemption codes. |
Assessment mutation tools (require Auth Token with MANAGER role)
| Tool | Schema | Description |
|------|--------|-------------|
| gql_create_assessment_attempt | CreateAssessmentAttempt(topicId, courseId, questions): AssessmentAttempt! | Create a super quiz attempt. |
Run with npx (stdio, for Claude Desktop / Cursor)
npx @thoughtindustries/thoughtindustriesAdd to your MCP config:
{
"mcpServers": {
"thoughtindustries": {
"command": "npx",
"args": ["-y", "@thoughtindustries/thoughtindustries"],
"env": {
"TI_BASE_URL": "https://yoursubdomain.thoughtindustries.com",
"TI_API_KEY": "your-api-key-from-settings-security",
"TI_HELIUM_USER_TOKEN": "optional-user-auth-token-for-learner-queries"
}
}
}
}Run with Docker
docker build -t thoughtindustries-mcp -f servers/thoughtindustries/Dockerfile .
docker run -i --rm \
-e TI_BASE_URL=https://yoursubdomain.thoughtindustries.com \
-e TI_API_KEY=your-api-key \
thoughtindustries-mcpClaude Desktop config (Docker)
{
"mcpServers": {
"thoughtindustries": {
"command": "docker",
"args": ["run", "-i", "--rm",
"-e", "TI_BASE_URL",
"-e", "TI_API_KEY",
"-e", "TI_HELIUM_USER_TOKEN",
"thoughtindustries-mcp:latest"
],
"env": {
"TI_BASE_URL": "https://yoursubdomain.thoughtindustries.com",
"TI_API_KEY": "your-api-key",
"TI_HELIUM_USER_TOKEN": "optional-user-token"
}
}
}
}Multi-tenant HTTP deployment (Cloud Run)
Run one shared server for your whole team. The server holds no credentials — each user's Claude Desktop / Cursor sends their own TI instance + API key as headers on every request (see Request headers). A fresh, fully isolated MCP server is built per request, so users on different TI instances never share state.
flowchart LR
A["User A (acme.ti.com, keyA)"] -->|"POST /mcp + X-TI-* headers"| CR["Cloud Run\nMCP_TRANSPORT=http\n(no TI creds)"]
B["User B (beta.ti.com, keyB)"] -->|"POST /mcp + X-TI-* headers"| CR
CR -->|"REST + GraphQL with A's creds"| TIA["acme.thoughtindustries.com"]
CR -->|"REST + GraphQL with B's creds"| TIB["beta.thoughtindustries.com"]Try it locally
docker build -t thoughtindustries-mcp -f servers/thoughtindustries/Dockerfile .
docker run --rm -p 8080:8080 -e MCP_TRANSPORT=http -e PORT=8080 thoughtindustries-mcp
# health check
curl http://localhost:8080/health
# call with per-request credentials
curl -X POST http://localhost:8080/mcp \
-H "Content-Type: application/json" \
-H "Accept: application/json, text/event-stream" \
-H "X-TI-Base-URL: https://yoursubdomain.thoughtindustries.com" \
-H "X-TI-Api-Key: your-api-key" \
-d '{"jsonrpc":"2.0","method":"tools/list","params":{},"id":1}'Deploy to Google Cloud Run
The server reads the PORT env var (Cloud Run injects 8080) automatically. No TI
credentials are set on the service.
# 1. Create an Artifact Registry repo (one-time)
gcloud artifacts repositories create mcps \
--repository-format=docker --location=us-central1 --project=YOUR_PROJECT_ID
# 2. Build, tag, push
gcloud auth configure-docker us-central1-docker.pkg.dev
docker build -t thoughtindustries-mcp -f servers/thoughtindustries/Dockerfile .
docker tag thoughtindustries-mcp \
us-central1-docker.pkg.dev/YOUR_PROJECT_ID/mcps/thoughtindustries-mcp:latest
docker push \
us-central1-docker.pkg.dev/YOUR_PROJECT_ID/mcps/thoughtindustries-mcp:latest
# 3. Deploy — only MCP_TRANSPORT=http, NO TI_* vars
gcloud run deploy thoughtindustries-mcp \
--image=us-central1-docker.pkg.dev/YOUR_PROJECT_ID/mcps/thoughtindustries-mcp:latest \
--region=us-central1 \
--set-env-vars=MCP_TRANSPORT=http \
--allow-unauthenticated \
--project=YOUR_PROJECT_IDOmit
--allow-unauthenticatedto require Google IAM identity tokens on every request (recommended for production). Note that TI credentials are always per-user; IAM only gates who may reach the service.
Per-user client config (Claude Desktop / Cursor)
Each user points at the shared URL and supplies their own credentials as headers. Credentials live only on the user's machine — never on the server.
{
"mcpServers": {
"thoughtindustries": {
"url": "https://YOUR_SERVICE-HASH-uc.a.run.app/mcp",
"headers": {
"X-TI-Base-URL": "https://yoursubdomain.thoughtindustries.com",
"X-TI-Api-Key": "your-api-key-from-settings-security",
"X-TI-Helium-User-Token": "optional-user-auth-token"
}
}
}
}If the Cloud Run service requires IAM auth, also add an identity token:
"headers": {
"Authorization": "Bearer <gcloud auth print-identity-token>",
"X-TI-Base-URL": "https://yoursubdomain.thoughtindustries.com",
"X-TI-Api-Key": "your-api-key-from-settings-security"
}API coverage
This server covers the full Thought Industries Incoming REST API v2:
| Area | Operations | |------|-----------| | Ping | 1 | | Events / Notifications | 32 | | Users | 23 | | Content & Courses | 17 | | Course Groups | 5 | | Courses / Sections / Lessons / Topics | 10 | | Clients (Organizations) | 14 | | Licenses | 5 | | Assessment Attempts | 3 | | Assignment Submissions | 4 | | Redemption Codes | 4 | | Coupons | 3 | | Ecommerce | 1 | | Meetings & Locations | 3 | | Learning Path Milestones | 1 | | Tags | 2 | | Jobs | 1 | | Logs | 6 | | Total | 135 |
