csa-skilljar 0.8.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- csa_skilljar-0.8.0/LICENSE +202 -0
- csa_skilljar-0.8.0/PKG-INFO +315 -0
- csa_skilljar-0.8.0/README.md +275 -0
- csa_skilljar-0.8.0/pyproject.toml +76 -0
- csa_skilljar-0.8.0/setup.cfg +4 -0
- csa_skilljar-0.8.0/src/csa_skilljar/__init__.py +11 -0
- csa_skilljar-0.8.0/src/csa_skilljar/auth.py +169 -0
- csa_skilljar-0.8.0/src/csa_skilljar/backend.py +2011 -0
- csa_skilljar-0.8.0/src/csa_skilljar/client.py +319 -0
- csa_skilljar-0.8.0/src/csa_skilljar/exceptions.py +54 -0
- csa_skilljar-0.8.0/src/csa_skilljar/mcp/__init__.py +1 -0
- csa_skilljar-0.8.0/src/csa_skilljar/mcp/_config.py +142 -0
- csa_skilljar-0.8.0/src/csa_skilljar/mcp/_schemas.py +455 -0
- csa_skilljar-0.8.0/src/csa_skilljar/mcp/_tools/__init__.py +20 -0
- csa_skilljar-0.8.0/src/csa_skilljar/mcp/_tools/_base.py +63 -0
- csa_skilljar-0.8.0/src/csa_skilljar/mcp/_tools/access.py +92 -0
- csa_skilljar-0.8.0/src/csa_skilljar/mcp/_tools/courses.py +172 -0
- csa_skilljar-0.8.0/src/csa_skilljar/mcp/_tools/enrolment.py +372 -0
- csa_skilljar-0.8.0/src/csa_skilljar/mcp/_tools/feedback.py +47 -0
- csa_skilljar-0.8.0/src/csa_skilljar/mcp/_tools/groups.py +425 -0
- csa_skilljar-0.8.0/src/csa_skilljar/mcp/_tools/lessons.py +266 -0
- csa_skilljar-0.8.0/src/csa_skilljar/mcp/_tools/publishing.py +362 -0
- csa_skilljar-0.8.0/src/csa_skilljar/mcp/_tools/question_banks.py +287 -0
- csa_skilljar-0.8.0/src/csa_skilljar/mcp/_tools/questions.py +284 -0
- csa_skilljar-0.8.0/src/csa_skilljar/mcp/_tools/quizzes.py +196 -0
- csa_skilljar-0.8.0/src/csa_skilljar/mcp/_tools/signup_fields.py +187 -0
- csa_skilljar-0.8.0/src/csa_skilljar/mcp/_tools/students.py +299 -0
- csa_skilljar-0.8.0/src/csa_skilljar/mcp/_tools/web_packages.py +282 -0
- csa_skilljar-0.8.0/src/csa_skilljar/mcp/cli.py +90 -0
- csa_skilljar-0.8.0/src/csa_skilljar/mcp/server.py +80 -0
- csa_skilljar-0.8.0/src/csa_skilljar/policy.py +201 -0
- csa_skilljar-0.8.0/src/csa_skilljar/py.typed +0 -0
- csa_skilljar-0.8.0/src/csa_skilljar/scopes.py +105 -0
- csa_skilljar-0.8.0/src/csa_skilljar.egg-info/PKG-INFO +315 -0
- csa_skilljar-0.8.0/src/csa_skilljar.egg-info/SOURCES.txt +74 -0
- csa_skilljar-0.8.0/src/csa_skilljar.egg-info/dependency_links.txt +1 -0
- csa_skilljar-0.8.0/src/csa_skilljar.egg-info/entry_points.txt +2 -0
- csa_skilljar-0.8.0/src/csa_skilljar.egg-info/requires.txt +16 -0
- csa_skilljar-0.8.0/src/csa_skilljar.egg-info/top_level.txt +1 -0
- csa_skilljar-0.8.0/tests/test_access_tools.py +50 -0
- csa_skilljar-0.8.0/tests/test_auth.py +174 -0
- csa_skilljar-0.8.0/tests/test_backend_conformance.py +62 -0
- csa_skilljar-0.8.0/tests/test_batch.py +60 -0
- csa_skilljar-0.8.0/tests/test_check_upstream.py +104 -0
- csa_skilljar-0.8.0/tests/test_cli.py +79 -0
- csa_skilljar-0.8.0/tests/test_config.py +112 -0
- csa_skilljar-0.8.0/tests/test_courses_read.py +55 -0
- csa_skilljar-0.8.0/tests/test_courses_tool.py +64 -0
- csa_skilljar-0.8.0/tests/test_courses_write.py +118 -0
- csa_skilljar-0.8.0/tests/test_descriptions.py +198 -0
- csa_skilljar-0.8.0/tests/test_enrolment.py +188 -0
- csa_skilljar-0.8.0/tests/test_error_translation.py +69 -0
- csa_skilljar-0.8.0/tests/test_exceptions.py +30 -0
- csa_skilljar-0.8.0/tests/test_feedback_tool.py +28 -0
- csa_skilljar-0.8.0/tests/test_groups.py +345 -0
- csa_skilljar-0.8.0/tests/test_install.py +123 -0
- csa_skilljar-0.8.0/tests/test_lessons_read.py +102 -0
- csa_skilljar-0.8.0/tests/test_lessons_write.py +148 -0
- csa_skilljar-0.8.0/tests/test_pagination.py +162 -0
- csa_skilljar-0.8.0/tests/test_parity.py +73 -0
- csa_skilljar-0.8.0/tests/test_policy.py +209 -0
- csa_skilljar-0.8.0/tests/test_protocol.py +271 -0
- csa_skilljar-0.8.0/tests/test_public_api.py +33 -0
- csa_skilljar-0.8.0/tests/test_publishing.py +246 -0
- csa_skilljar-0.8.0/tests/test_question_banks.py +184 -0
- csa_skilljar-0.8.0/tests/test_questions.py +181 -0
- csa_skilljar-0.8.0/tests/test_quizzes.py +168 -0
- csa_skilljar-0.8.0/tests/test_scopes.py +40 -0
- csa_skilljar-0.8.0/tests/test_signup_fields.py +176 -0
- csa_skilljar-0.8.0/tests/test_stdout_guard.py +15 -0
- csa_skilljar-0.8.0/tests/test_students.py +184 -0
- csa_skilljar-0.8.0/tests/test_tool_errors.py +36 -0
- csa_skilljar-0.8.0/tests/test_tool_naming.py +45 -0
- csa_skilljar-0.8.0/tests/test_v2backend.py +167 -0
- csa_skilljar-0.8.0/tests/test_web_packages.py +304 -0
- csa_skilljar-0.8.0/tests/test_zero_defect.py +212 -0
|
@@ -0,0 +1,202 @@
|
|
|
1
|
+
Apache License
|
|
2
|
+
Version 2.0, January 2004
|
|
3
|
+
http://www.apache.org/licenses/
|
|
4
|
+
|
|
5
|
+
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
|
6
|
+
|
|
7
|
+
1. Definitions.
|
|
8
|
+
|
|
9
|
+
"License" shall mean the terms and conditions for use, reproduction,
|
|
10
|
+
and distribution as defined by Sections 1 through 9 of this document.
|
|
11
|
+
|
|
12
|
+
"Licensor" shall mean the copyright owner or entity authorized by
|
|
13
|
+
the copyright owner that is granting the License.
|
|
14
|
+
|
|
15
|
+
"Legal Entity" shall mean the union of the acting entity and all
|
|
16
|
+
other entities that control, are controlled by, or are under common
|
|
17
|
+
control with that entity. For the purposes of this definition,
|
|
18
|
+
"control" means (i) the power, direct or indirect, to cause the
|
|
19
|
+
direction or management of such entity, whether by contract or
|
|
20
|
+
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
|
21
|
+
outstanding shares, or (iii) beneficial ownership of such entity.
|
|
22
|
+
|
|
23
|
+
"You" (or "Your") shall mean an individual or Legal Entity
|
|
24
|
+
exercising permissions granted by this License.
|
|
25
|
+
|
|
26
|
+
"Source" form shall mean the preferred form for making modifications,
|
|
27
|
+
including but not limited to software source code, documentation
|
|
28
|
+
source, and configuration files.
|
|
29
|
+
|
|
30
|
+
"Object" form shall mean any form resulting from mechanical
|
|
31
|
+
transformation or translation of a Source form, including but
|
|
32
|
+
not limited to compiled object code, generated documentation,
|
|
33
|
+
and conversions to other media types.
|
|
34
|
+
|
|
35
|
+
"Work" shall mean the work of authorship, whether in Source or
|
|
36
|
+
Object form, made available under the License, as indicated by a
|
|
37
|
+
copyright notice that is included in or attached to the work
|
|
38
|
+
(an example is provided in the Appendix below).
|
|
39
|
+
|
|
40
|
+
"Derivative Works" shall mean any work, whether in Source or Object
|
|
41
|
+
form, that is based on (or derived from) the Work and for which the
|
|
42
|
+
editorial revisions, annotations, elaborations, or other modifications
|
|
43
|
+
represent, as a whole, an original work of authorship. For the purposes
|
|
44
|
+
of this License, Derivative Works shall not include works that remain
|
|
45
|
+
separable from, or merely link (or bind by name) to the interfaces of,
|
|
46
|
+
the Work and Derivative Works thereof.
|
|
47
|
+
|
|
48
|
+
"Contribution" shall mean any work of authorship, including
|
|
49
|
+
the original version of the Work and any modifications or additions
|
|
50
|
+
to that Work or Derivative Works thereof, that is intentionally
|
|
51
|
+
submitted to Licensor for inclusion in the Work by the copyright owner
|
|
52
|
+
or by an individual or Legal Entity authorized to submit on behalf of
|
|
53
|
+
the copyright owner. For the purposes of this definition, "submitted"
|
|
54
|
+
means any form of electronic, verbal, or written communication sent
|
|
55
|
+
to the Licensor or its representatives, including but not limited to
|
|
56
|
+
communication on electronic mailing lists, source code control systems,
|
|
57
|
+
and issue tracking systems that are managed by, or on behalf of, the
|
|
58
|
+
Licensor for the purpose of discussing and improving the Work, but
|
|
59
|
+
excluding communication that is conspicuously marked or otherwise
|
|
60
|
+
designated in writing by the copyright owner as "Not a Contribution."
|
|
61
|
+
|
|
62
|
+
"Contributor" shall mean Licensor and any individual or Legal Entity
|
|
63
|
+
on behalf of whom a Contribution has been received by Licensor and
|
|
64
|
+
subsequently incorporated within the Work.
|
|
65
|
+
|
|
66
|
+
2. Grant of Copyright License. Subject to the terms and conditions of
|
|
67
|
+
this License, each Contributor hereby grants to You a perpetual,
|
|
68
|
+
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
|
69
|
+
copyright license to reproduce, prepare Derivative Works of,
|
|
70
|
+
publicly display, publicly perform, sublicense, and distribute the
|
|
71
|
+
Work and such Derivative Works in Source or Object form.
|
|
72
|
+
|
|
73
|
+
3. Grant of Patent License. Subject to the terms and conditions of
|
|
74
|
+
this License, each Contributor hereby grants to You a perpetual,
|
|
75
|
+
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
|
76
|
+
(except as stated in this section) patent license to make, have made,
|
|
77
|
+
use, offer to sell, sell, import, and otherwise transfer the Work,
|
|
78
|
+
where such license applies only to those patent claims licensable
|
|
79
|
+
by such Contributor that are necessarily infringed by their
|
|
80
|
+
Contribution(s) alone or by combination of their Contribution(s)
|
|
81
|
+
with the Work to which such Contribution(s) was submitted. If You
|
|
82
|
+
institute patent litigation against any entity (including a
|
|
83
|
+
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
|
84
|
+
or a Contribution incorporated within the Work constitutes direct
|
|
85
|
+
or contributory patent infringement, then any patent licenses
|
|
86
|
+
granted to You under this License for that Work shall terminate
|
|
87
|
+
as of the date such litigation is filed.
|
|
88
|
+
|
|
89
|
+
4. Redistribution. You may reproduce and distribute copies of the
|
|
90
|
+
Work or Derivative Works thereof in any medium, with or without
|
|
91
|
+
modifications, and in Source or Object form, provided that You
|
|
92
|
+
meet the following conditions:
|
|
93
|
+
|
|
94
|
+
(a) You must give any other recipients of the Work or
|
|
95
|
+
Derivative Works a copy of this License; and
|
|
96
|
+
|
|
97
|
+
(b) You must cause any modified files to carry prominent notices
|
|
98
|
+
stating that You changed the files; and
|
|
99
|
+
|
|
100
|
+
(c) You must retain, in the Source form of any Derivative Works
|
|
101
|
+
that You distribute, all copyright, patent, trademark, and
|
|
102
|
+
attribution notices from the Source form of the Work,
|
|
103
|
+
excluding those notices that do not pertain to any part of
|
|
104
|
+
the Derivative Works; and
|
|
105
|
+
|
|
106
|
+
(d) If the Work includes a "NOTICE" text file as part of its
|
|
107
|
+
distribution, then any Derivative Works that You distribute must
|
|
108
|
+
include a readable copy of the attribution notices contained
|
|
109
|
+
within such NOTICE file, excluding those notices that do not
|
|
110
|
+
pertain to any part of the Derivative Works, in at least one
|
|
111
|
+
of the following places: within a NOTICE text file distributed
|
|
112
|
+
as part of the Derivative Works; within the Source form or
|
|
113
|
+
documentation, if provided along with the Derivative Works; or,
|
|
114
|
+
within a display generated by the Derivative Works, if and
|
|
115
|
+
wherever such third-party notices normally appear. The contents
|
|
116
|
+
of the NOTICE file are for informational purposes only and
|
|
117
|
+
do not modify the License. You may add Your own attribution
|
|
118
|
+
notices within Derivative Works that You distribute, alongside
|
|
119
|
+
or as an addendum to the NOTICE text from the Work, provided
|
|
120
|
+
that such additional attribution notices cannot be construed
|
|
121
|
+
as modifying the License.
|
|
122
|
+
|
|
123
|
+
You may add Your own copyright statement to Your modifications and
|
|
124
|
+
may provide additional or different license terms and conditions
|
|
125
|
+
for use, reproduction, or distribution of Your modifications, or
|
|
126
|
+
for any such Derivative Works as a whole, provided Your use,
|
|
127
|
+
reproduction, and distribution of the Work otherwise complies with
|
|
128
|
+
the conditions stated in this License.
|
|
129
|
+
|
|
130
|
+
5. Submission of Contributions. Unless You explicitly state otherwise,
|
|
131
|
+
any Contribution intentionally submitted for inclusion in the Work
|
|
132
|
+
by You to the Licensor shall be under the terms and conditions of
|
|
133
|
+
this License, without any additional terms or conditions.
|
|
134
|
+
Notwithstanding the above, nothing herein shall supersede or modify
|
|
135
|
+
the terms of any separate license agreement you may have executed
|
|
136
|
+
with Licensor regarding such Contributions.
|
|
137
|
+
|
|
138
|
+
6. Trademarks. This License does not grant permission to use the trade
|
|
139
|
+
names, trademarks, service marks, or product names of the Licensor,
|
|
140
|
+
except as required for reasonable and customary use in describing the
|
|
141
|
+
origin of the Work and reproducing the content of the NOTICE file.
|
|
142
|
+
|
|
143
|
+
7. Disclaimer of Warranty. Unless required by applicable law or
|
|
144
|
+
agreed to in writing, Licensor provides the Work (and each
|
|
145
|
+
Contributor provides its Contributions) on an "AS IS" BASIS,
|
|
146
|
+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
|
147
|
+
implied, including, without limitation, any warranties or conditions
|
|
148
|
+
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
|
149
|
+
PARTICULAR PURPOSE. You are solely responsible for determining the
|
|
150
|
+
appropriateness of using or redistributing the Work and assume any
|
|
151
|
+
risks associated with Your exercise of permissions under this License.
|
|
152
|
+
|
|
153
|
+
8. Limitation of Liability. In no event and under no legal theory,
|
|
154
|
+
whether in tort (including negligence), contract, or otherwise,
|
|
155
|
+
unless required by applicable law (such as deliberate and grossly
|
|
156
|
+
negligent acts) or agreed to in writing, shall any Contributor be
|
|
157
|
+
liable to You for damages, including any direct, indirect, special,
|
|
158
|
+
incidental, or consequential damages of any character arising as a
|
|
159
|
+
result of this License or out of the use or inability to use the
|
|
160
|
+
Work (including but not limited to damages for loss of goodwill,
|
|
161
|
+
work stoppage, computer failure or malfunction, or any and all
|
|
162
|
+
other commercial damages or losses), even if such Contributor
|
|
163
|
+
has been advised of the possibility of such damages.
|
|
164
|
+
|
|
165
|
+
9. Accepting Warranty or Additional Liability. While redistributing
|
|
166
|
+
the Work or Derivative Works thereof, You may choose to offer,
|
|
167
|
+
and charge a fee for, acceptance of support, warranty, indemnity,
|
|
168
|
+
or other liability obligations and/or rights consistent with this
|
|
169
|
+
License. However, in accepting such obligations, You may act only
|
|
170
|
+
on Your own behalf and on Your sole responsibility, not on behalf
|
|
171
|
+
of any other Contributor, and only if You agree to indemnify,
|
|
172
|
+
defend, and hold each Contributor harmless for any liability
|
|
173
|
+
incurred by, or claims asserted against, such Contributor by reason
|
|
174
|
+
of your accepting any such warranty or additional liability.
|
|
175
|
+
|
|
176
|
+
END OF TERMS AND CONDITIONS
|
|
177
|
+
|
|
178
|
+
APPENDIX: How to apply the Apache License to your work.
|
|
179
|
+
|
|
180
|
+
To apply the Apache License to your work, attach the following
|
|
181
|
+
boilerplate notice, with the fields enclosed by brackets "[]"
|
|
182
|
+
replaced with your own identifying information. (Don't include
|
|
183
|
+
the brackets!) The text should be enclosed in the appropriate
|
|
184
|
+
comment syntax for the file format. We also recommend that a
|
|
185
|
+
file or class name and description of purpose be included on the
|
|
186
|
+
same "printed page" as the copyright notice for easier
|
|
187
|
+
identification within third-party archives.
|
|
188
|
+
|
|
189
|
+
Copyright 2025 Kurt Seifried kseifried@cloudsecurityalliance.org
|
|
190
|
+
|
|
191
|
+
Licensed under the Apache License, Version 2.0 (the "License");
|
|
192
|
+
you may not use this file except in compliance with the License.
|
|
193
|
+
You may obtain a copy of the License at
|
|
194
|
+
|
|
195
|
+
http://www.apache.org/licenses/LICENSE-2.0
|
|
196
|
+
|
|
197
|
+
Unless required by applicable law or agreed to in writing, software
|
|
198
|
+
distributed under the License is distributed on an "AS IS" BASIS,
|
|
199
|
+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
200
|
+
See the License for the specific language governing permissions and
|
|
201
|
+
limitations under the License.
|
|
202
|
+
|
|
@@ -0,0 +1,315 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: csa-skilljar
|
|
3
|
+
Version: 0.8.0
|
|
4
|
+
Summary: Python client and local MCP server for both Skilljar REST APIs
|
|
5
|
+
Author: Cloud Security Alliance
|
|
6
|
+
Maintainer-email: Kurt Seifried <kseifried@cloudsecurityalliance.org>
|
|
7
|
+
License-Expression: Apache-2.0
|
|
8
|
+
Project-URL: Homepage, https://github.com/CloudSecurityAlliance/csa-skilljar
|
|
9
|
+
Project-URL: Issues, https://github.com/CloudSecurityAlliance/csa-skilljar/issues
|
|
10
|
+
Keywords: skilljar,lms,training,mcp,elearning,gainsight
|
|
11
|
+
Classifier: Development Status :: 3 - Alpha
|
|
12
|
+
Classifier: Intended Audience :: Developers
|
|
13
|
+
Classifier: Operating System :: OS Independent
|
|
14
|
+
Classifier: Programming Language :: Python :: 3
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.14
|
|
20
|
+
Classifier: Topic :: Education
|
|
21
|
+
Classifier: Typing :: Typed
|
|
22
|
+
Requires-Python: >=3.10
|
|
23
|
+
Description-Content-Type: text/markdown
|
|
24
|
+
License-File: LICENSE
|
|
25
|
+
Requires-Dist: httpx>=0.27
|
|
26
|
+
Requires-Dist: typing_extensions>=4.7
|
|
27
|
+
Requires-Dist: mcp>=2.1
|
|
28
|
+
Provides-Extra: mcp
|
|
29
|
+
Provides-Extra: dev
|
|
30
|
+
Requires-Dist: pytest>=8; extra == "dev"
|
|
31
|
+
Requires-Dist: pytest-cov>=5; extra == "dev"
|
|
32
|
+
Requires-Dist: ruff>=0.6; extra == "dev"
|
|
33
|
+
Requires-Dist: mypy>=1.11; extra == "dev"
|
|
34
|
+
Requires-Dist: respx>=0.21; extra == "dev"
|
|
35
|
+
Requires-Dist: anyio>=4; extra == "dev"
|
|
36
|
+
Requires-Dist: build>=1.2; extra == "dev"
|
|
37
|
+
Requires-Dist: bandit>=1.7; extra == "dev"
|
|
38
|
+
Requires-Dist: pip-audit>=2.7; extra == "dev"
|
|
39
|
+
Dynamic: license-file
|
|
40
|
+
|
|
41
|
+
```
|
|
42
|
+
project_tracker_base: CINO Project Tracker:appf7fRQUvY9Iy7sL
|
|
43
|
+
project_tracker_table: Projects:tblchmbxSAavvJKaY
|
|
44
|
+
project_tracker_record: csa-skilljar:recbwfx6O30BboQNv
|
|
45
|
+
project_source: github:CloudSecurityAlliance-Internal/CINO-Projects/projects/CloudSecurityAlliance/csa-skilljar
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
# csa-skilljar
|
|
49
|
+
|
|
50
|
+
A Python library and local MCP server for the [Skilljar](https://www.skilljar.com/) customer
|
|
51
|
+
education platform, covering **both** of Skilljar's REST APIs — v1 and v2 — behind one set of
|
|
52
|
+
tools.
|
|
53
|
+
|
|
54
|
+
> **Status: Blocks 1–9 implemented — full 73-tool parity — not yet released.**
|
|
55
|
+
>
|
|
56
|
+
> **76 tools** over Skilljar's v2 API. Install from source until the first PyPI release:
|
|
57
|
+
> `pipx install git+https://github.com/CloudSecurityAlliance/csa-skilljar`
|
|
58
|
+
> | | |
|
|
59
|
+
> |---|---|
|
|
60
|
+
> | **Server** | `check_access` · `describe_capabilities` · `report_a_problem` |
|
|
61
|
+
> | **Courses** | `list_courses` · `get_course` · `create_courses` · `update_courses` |
|
|
62
|
+
> | **Lessons** | `list_lessons` · `get_lesson` · `create_lessons` · `update_lessons` |
|
|
63
|
+
> | **Quizzes** | `list_quizzes` · `get_quiz` · `create_quizzes` · `update_quizzes` · `delete_quizzes` |
|
|
64
|
+
> | **Questions** | `list_questions` · `get_question` · `create_questions` · `update_questions` · `delete_questions` |
|
|
65
|
+
> | **Question banks** | `list_question_banks` · `get_question_bank` · `create_question_banks` · `update_question_banks` · `delete_question_banks` |
|
|
66
|
+
> | **Bank bindings** | `list_quiz_question_bank_assignments` · `bind_quiz_question_banks` · `update_quiz_question_banks` · `unbind_quiz_question_banks` |
|
|
67
|
+
> | **Enrolment** | `list_enrollments` · `get_enrollment` · `update_enrollments` · `complete_enrollments` · `bulk_enroll_students` |
|
|
68
|
+
> | **Reporting** | `list_certificates` · `get_certificate` · `get_course_analytics` · `list_course_ratings` |
|
|
69
|
+
> | **Students** | `list_students` · `get_student` · `create_students` · `update_students` |
|
|
70
|
+
> | **Groups** | `list_groups` · `get_group` · `create_groups` · `update_groups` · `add_group_memberships` · `remove_group_memberships` |
|
|
71
|
+
> | **Signup fields** | `list_signup_field_values` · `get_signup_field_value` · `create_signup_field_values` · `update_signup_field_values` |
|
|
72
|
+
> | **Publishing** | `list_published_courses` · `get_published_course` · `publish_courses` · `update_published_courses` |
|
|
73
|
+
> | **Catalog** | `list_domains` · `get_domain` |
|
|
74
|
+
> | **Course visibility** | `list_visibility_overrides` · `add_visibility_overrides` · `remove_visibility_overrides` |
|
|
75
|
+
> | **Web packages** | `list_web_packages` · `get_web_package` · `create_web_packages` · `update_web_packages` |
|
|
76
|
+
> | **Students (destructive)** | `anonymize_student` · `deactivate_student` · `set_student_password` · `send_password_reset` — gated on `people.destructive`, which no profile but `full` grants |
|
|
77
|
+
> | **Groups (destructive)** | `delete_groups` — a hard delete that cascades to memberships and course visibility; gated on `groups.delete` |
|
|
78
|
+
> | **Publishing (public-facing)** | `unpublish_published_course` · `republish_published_course` · `delete_published_course` — gated on `publishing.write`, which `authoring` does not grant |
|
|
79
|
+
> | **Web packages (destructive)** | `delete_web_package` — refused while a live lesson uses the package |
|
|
80
|
+
> | **Credentials** | `register_oauth_client` — mints an OAuth client; off unless the `admin` profile is named |
|
|
81
|
+
>
|
|
82
|
+
> All 73 official tools are present — asserted by `tests/test_parity.py`, not claimed. The three extra tools are our own server management. See [ROADMAP.md](ROADMAP.md).
|
|
83
|
+
|
|
84
|
+
## Start with Skilljar's official MCP server
|
|
85
|
+
|
|
86
|
+
**If you want Skilljar in an AI client, use [Skilljar's own MCP server](https://mcp.skilljar.com/mcp).**
|
|
87
|
+
That is the right default and we recommend it without reservation. It is first-party, hosted and
|
|
88
|
+
maintained by the vendor, needs nothing installed on your machine, covers the whole v2 API in 73
|
|
89
|
+
tools, and authenticates with OAuth and per-operation scopes. Skilljar are actively building v2
|
|
90
|
+
out, so it gets better on their release cadence rather than ours.
|
|
91
|
+
|
|
92
|
+
```bash
|
|
93
|
+
claude mcp add skilljar --transport http https://mcp.skilljar.com/mcp
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
Try that first. For most people it is the whole answer.
|
|
97
|
+
|
|
98
|
+
## …but if you need more
|
|
99
|
+
|
|
100
|
+
Some things are not in the v2 API yet, so no v2 client can reach them. The v1 API is considerably
|
|
101
|
+
larger — 340 operations against v2's 82 — and today it is the only way to get at:
|
|
102
|
+
|
|
103
|
+
- **per-lesson learner progress** — v2 reports course-level completion only
|
|
104
|
+
- **webhooks** — v2 has no event notifications at all
|
|
105
|
+
- **asset upload** — v2 has no file upload
|
|
106
|
+
- **learning paths**, **instructor-led training**, and the **commerce stack**
|
|
107
|
+
(offers, promo codes, purchases, training credits)
|
|
108
|
+
|
|
109
|
+
`csa-skilljar` exists for that gap. It reproduces the official tool surface exactly — same tool
|
|
110
|
+
names, same argument names — and then adds the v1-only capabilities alongside them, so you do not
|
|
111
|
+
have to choose between the two APIs or run two servers.
|
|
112
|
+
|
|
113
|
+
It also runs locally over stdio, which some organisations need: your API credentials stay on your
|
|
114
|
+
own machine.
|
|
115
|
+
|
|
116
|
+
**We expect this project to shrink over time, and that is the intended outcome.** Skilljar has
|
|
117
|
+
publicly reserved OAuth scopes for webhooks, paths, assets, tags and commerce. As those endpoints
|
|
118
|
+
ship, the corresponding v1 support here gets retired in favour of v2 — the tool names stay the
|
|
119
|
+
same and callers notice nothing.
|
|
120
|
+
|
|
121
|
+
| | Official Skilljar MCP | csa-skilljar |
|
|
122
|
+
|---|---|---|
|
|
123
|
+
| APIs | v2 | v1 + v2 |
|
|
124
|
+
| Transport | remote HTTP | local stdio |
|
|
125
|
+
| Credentials | held server-side | stay on your machine |
|
|
126
|
+
| Auth | OAuth authorization code (browser) | OAuth client credentials + v1 API key |
|
|
127
|
+
| Capability control | OAuth scopes at consent | scopes **plus** per-install profiles |
|
|
128
|
+
| Library | — | the library is the product too |
|
|
129
|
+
|
|
130
|
+
## Checking the state of Skilljar's v2 API
|
|
131
|
+
|
|
132
|
+
Skilljar's v2 API is actively growing, and the official MCP server tracks it closely. That is good
|
|
133
|
+
news for everyone — and it means the coverage map in this project has a shelf life. **Before
|
|
134
|
+
assuming a gap documented here is still a gap, re-check upstream.**
|
|
135
|
+
|
|
136
|
+
```bash
|
|
137
|
+
# 1. The published v2 surface
|
|
138
|
+
curl -s https://api.skilljar.com/v2/openapi.json | jq '.paths | keys | length'
|
|
139
|
+
|
|
140
|
+
# 2. The declared scope catalogue — a leading indicator of what is coming
|
|
141
|
+
curl -s https://api.skilljar.com/.well-known/oauth-authorization-server \
|
|
142
|
+
| jq -r '.scopes_supported[]'
|
|
143
|
+
|
|
144
|
+
# 3. The official MCP server's live tool list
|
|
145
|
+
# Connect it, then run /mcp in Claude Code.
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
At the time of writing, the scope catalogue advertises **88 scopes** while the published v2 spec
|
|
149
|
+
uses **28** — areas including webhooks, paths, assets, tags and commerce have scopes reserved but
|
|
150
|
+
no endpoints yet. When those endpoints ship, the v1 fallbacks this project provides for them
|
|
151
|
+
should be retired in favour of v2, and this note updated.
|
|
152
|
+
|
|
153
|
+
`scripts/check_upstream.py` will automate all three and report drift against the snapshots in
|
|
154
|
+
`specs/`. Until then, run the commands above.
|
|
155
|
+
|
|
156
|
+
## Credentials
|
|
157
|
+
|
|
158
|
+
Two independent credentials, both optional. The server starts with either, both, or neither, and
|
|
159
|
+
tells you what is available.
|
|
160
|
+
|
|
161
|
+
| Variable | For | Obtain from |
|
|
162
|
+
|---|---|---|
|
|
163
|
+
| `CSA_SKILLJAR_V1_API_KEY` | the v1 API | Skilljar Dashboard — see [Skilljar's API guide](https://support.gainsight.com/Skilljar/Develop_and_Customize/API/Getting_started_with_the_Skilljar_API) |
|
|
164
|
+
| `CSA_SKILLJAR_V2_CLIENT_ID` / `CSA_SKILLJAR_V2_CLIENT_SECRET` | the v2 API | Skilljar Dashboard, v2 API clients |
|
|
165
|
+
|
|
166
|
+
We link Skilljar's own documentation rather than transcribing their dashboard navigation, which
|
|
167
|
+
we cannot keep current.
|
|
168
|
+
|
|
169
|
+
### Connecting it to an MCP client
|
|
170
|
+
|
|
171
|
+
```bash
|
|
172
|
+
# Recommended: credentials stay in .env, and the client config holds no secret.
|
|
173
|
+
claude mcp add csa-skilljar -- /abs/path/to/csa-skilljar/scripts/mcp-launch.sh
|
|
174
|
+
|
|
175
|
+
# Or pass them directly - note this writes the literal secret into ~/.claude.json,
|
|
176
|
+
# which is not gitignored, and into your shell history.
|
|
177
|
+
claude mcp add csa-skilljar \
|
|
178
|
+
-e CSA_SKILLJAR_V2_CLIENT_ID=... -e CSA_SKILLJAR_V2_CLIENT_SECRET=... \
|
|
179
|
+
-- /abs/path/to/csa-skilljar/.venv/bin/csa-skilljar-mcp
|
|
180
|
+
```
|
|
181
|
+
|
|
182
|
+
`scripts/mcp-launch.sh` reads `CSA_SKILLJAR_*` variables from the repository's `.env`
|
|
183
|
+
(override with `CSA_SKILLJAR_ENV_FILE`) and execs the server. It parses the file rather
|
|
184
|
+
than sourcing it: `source` executes it, so a stray `echo` would print to stdout and
|
|
185
|
+
corrupt the JSON-RPC stream before the server ever starts.
|
|
186
|
+
|
|
187
|
+
Use an **absolute path** to the script or to `.venv/bin/csa-skilljar-mcp`. A bare
|
|
188
|
+
`csa-skilljar-mcp` resolves through `PATH`, which may find a different install.
|
|
189
|
+
|
|
190
|
+
Then call `check_access` first — it is built to work when nothing else does, and reports
|
|
191
|
+
which credentials resolved and which scopes the token carries.
|
|
192
|
+
|
|
193
|
+
**There is no login step and no browser.** The v2 credential is a machine credential: you create
|
|
194
|
+
an API client in the Skilljar Dashboard, put its id and secret in your MCP client's configuration,
|
|
195
|
+
and the server obtains its own access token on first use (`client_credentials`, ADR-003). No
|
|
196
|
+
redirect URI, no consent screen, no token file on disk. Skilljar's own hosted MCP server does use
|
|
197
|
+
an interactive flow — it is remote and acts for a browser user, which is exactly the constraint
|
|
198
|
+
running locally removes.
|
|
199
|
+
|
|
200
|
+
Scope the v2 client to what you actually need. The API declares a required scope on every
|
|
201
|
+
operation, and the sensitive ones are separable — `students:anonymize` (irreversible),
|
|
202
|
+
`students:deactivate`, and `students:manage-password` can all be withheld from a client used for
|
|
203
|
+
content authoring.
|
|
204
|
+
|
|
205
|
+
## What it will cover
|
|
206
|
+
|
|
207
|
+
Reproduces all 73 official tools, then adds v1-only families in this order:
|
|
208
|
+
|
|
209
|
+
1. **Learner progress** — per-lesson detail, which v2 does not report
|
|
210
|
+
2. **Assets & media** — v2 has no file upload
|
|
211
|
+
3. **Commerce** — offers, promo codes, purchases, training credits
|
|
212
|
+
4. **Learning paths** — paths, path items, path enrolments
|
|
213
|
+
5. **Events & webhooks** — subscriptions and payload previews
|
|
214
|
+
6. **Instructor-led training** — sessions, instructors, registrations
|
|
215
|
+
7. **Labels & tags**
|
|
216
|
+
|
|
217
|
+
Deliberately out of scope: catalog page-building, webhook *receiving*, caching, and cross-API
|
|
218
|
+
composite writes. Reasons are in the spec.
|
|
219
|
+
|
|
220
|
+
## Project documentation
|
|
221
|
+
|
|
222
|
+
| File | What it answers |
|
|
223
|
+
|---|---|
|
|
224
|
+
| [Design spec](docs/superpowers/specs/2026-08-26-csa-skilljar-design.md) | Architecture, routing rule, credential model, auth error taxonomy, phasing. **Start here.** |
|
|
225
|
+
| [ROADMAP.md](ROADMAP.md) | The block sequence — what ships in what order, and what is parked |
|
|
226
|
+
| [GOALS.md](GOALS.md) | What success looks like and how we would know it failed |
|
|
227
|
+
| [BUSINESS-CASE.md](BUSINESS-CASE.md) | Why CSA is investing, and the honest case that this project should shrink |
|
|
228
|
+
| [TODO.md](TODO.md) | Index of all open work |
|
|
229
|
+
| [DECISIONS-ADR.md](DECISIONS-ADR.md) | Technical decisions and why the rejected alternatives lost |
|
|
230
|
+
| [DECISIONS-PRD.md](DECISIONS-PRD.md) | Scope, audience, and what is deliberately out |
|
|
231
|
+
| [SECURITY-RESOURCES.md](SECURITY-RESOURCES.md) | Exposure surface, prompt-injection risk, credential custody |
|
|
232
|
+
| [DATA-RESOURCES.md](DATA-RESOURCES.md) | What data this handles, and what it deliberately never stores |
|
|
233
|
+
| [WAITING-FOR.md](WAITING-FOR.md) | External conditions we are waiting on, each with an observable trigger |
|
|
234
|
+
| [FRICTION.md](FRICTION.md) | Work that is harder than it should be — including how this project works with AI |
|
|
235
|
+
| [RACI.md](RACI.md) | Who decides what |
|
|
236
|
+
| [CLAUDE.md](CLAUDE.md) | Behavioural contract for AI agents working here |
|
|
237
|
+
|
|
238
|
+
## Development
|
|
239
|
+
|
|
240
|
+
**Always use a virtual environment.** The interpreter is pinned by `.python-version`.
|
|
241
|
+
|
|
242
|
+
```bash
|
|
243
|
+
python3 -m venv .venv
|
|
244
|
+
.venv/bin/python -m pip install -e ".[dev]"
|
|
245
|
+
|
|
246
|
+
.venv/bin/python -m pytest -q # offline suite: no network, no credentials
|
|
247
|
+
.venv/bin/ruff check src tests scripts
|
|
248
|
+
.venv/bin/mypy
|
|
249
|
+
|
|
250
|
+
./scripts/verify.sh # or just this: everything CI checks
|
|
251
|
+
```
|
|
252
|
+
|
|
253
|
+
Commands are written `.venv/bin/...` deliberately — a bare `pytest` resolves to whatever
|
|
254
|
+
is on `PATH`, which is how a suite passes against the wrong dependency versions.
|
|
255
|
+
|
|
256
|
+
Contributions follow [CSA's public repo standards](https://github.com/CloudSecurityAlliance-Internal/CINO-Platform-Engineering/blob/main/PUBLIC-GITHUB-REPO-STANDARDS.md):
|
|
257
|
+
branch and PR for every change, required CI gates, no direct pushes to `main`.
|
|
258
|
+
|
|
259
|
+
## Releasing
|
|
260
|
+
|
|
261
|
+
Publishing uses **PyPI Trusted Publishing** — GitHub Actions authenticates over OIDC and
|
|
262
|
+
proves its identity with the repository, workflow and environment it is running in.
|
|
263
|
+
**There is no API token anywhere**: not in the repository, not in a GitHub secret, not in
|
|
264
|
+
a `.pypirc`. Nothing to leak, rotate, or accidentally commit.
|
|
265
|
+
|
|
266
|
+
The identity PyPI checks is exactly this:
|
|
267
|
+
|
|
268
|
+
| Field | Value |
|
|
269
|
+
|---|---|
|
|
270
|
+
| PyPI project | `csa-skilljar` |
|
|
271
|
+
| Owner | `CloudSecurityAlliance` |
|
|
272
|
+
| Repository | `csa-skilljar` |
|
|
273
|
+
| Workflow | `release.yml` |
|
|
274
|
+
| Environment | `pypi` |
|
|
275
|
+
|
|
276
|
+
**One-time setup** (a person with the PyPI account, per `RACI.md` — credential and
|
|
277
|
+
publishing identity are not delegated): at
|
|
278
|
+
<https://pypi.org/manage/account/publishing/>, add a **pending publisher** with the five
|
|
279
|
+
values above. "Pending" is the form used when the project does not exist on PyPI yet; it
|
|
280
|
+
becomes a normal trusted publisher on first upload.
|
|
281
|
+
|
|
282
|
+
**Each release:**
|
|
283
|
+
|
|
284
|
+
```bash
|
|
285
|
+
# 1. Bump the single source of truth and refresh the editable install.
|
|
286
|
+
# src/csa_skilljar/__init__.py __version__ = "X.Y.Z"
|
|
287
|
+
.venv/bin/python -m pip install -e ".[dev]"
|
|
288
|
+
./scripts/verify.sh
|
|
289
|
+
|
|
290
|
+
# 2. Merge, then tag from main. The tag MUST equal the packaged version - the
|
|
291
|
+
# workflow refuses to publish when they disagree, rather than shipping a
|
|
292
|
+
# mislabelled artifact.
|
|
293
|
+
git tag vX.Y.Z && git push origin vX.Y.Z
|
|
294
|
+
gh release create vX.Y.Z --notes-from-tag
|
|
295
|
+
```
|
|
296
|
+
|
|
297
|
+
Publishing the GitHub release starts `release.yml`, which reruns the tests, `pip-audit`
|
|
298
|
+
and `bandit`, checks the tag against the packaged version, builds, and refuses to upload
|
|
299
|
+
an artifact containing anything matching `.env`, `token`, `secret`, `credential`,
|
|
300
|
+
`analysis/` or `docs-html/`, or missing `py.typed`.
|
|
301
|
+
|
|
302
|
+
It then waits: the `pypi` environment has a **required reviewer**, so the upload does not
|
|
303
|
+
happen until a human approves it in the Actions run. Worth knowing that GitHub creates a
|
|
304
|
+
missing environment *unprotected* on first use — so `environment: pypi` in a workflow is
|
|
305
|
+
a claim, not a control, until the environment actually exists with rules on it.
|
|
306
|
+
|
|
307
|
+
## Licence
|
|
308
|
+
|
|
309
|
+
[Apache-2.0](LICENSE).
|
|
310
|
+
|
|
311
|
+
## Acknowledgements
|
|
312
|
+
|
|
313
|
+
Skilljar is a Gainsight product. This project is not affiliated with or endorsed by Skilljar or
|
|
314
|
+
Gainsight; it is an independent client built against their public APIs. The API snapshots in
|
|
315
|
+
`specs/` are fetched from Skilljar's published, publicly accessible OpenAPI documents.
|