Recipes

Instructor agent access for Artemis: scoped personal access tokens plus a 23-tool MCP server

Discovered from README cross-links · yesterday

Software & DevOpsWork & Operations

Scoped tokens and an MCP endpoint let coding agents work on course content

What they did

The author added an opt-in agent access layer to Artemis, a Java/Spring + Angular teaching platform, in nine readable commits. First, personal access tokens on the server: created in the web UI, shown once, stored only as a SHA-256 hash, expiring after at most 90 days, revocable, authenticating as their owner (never as admin) and reaching only endpoints annotated with @AllowedScopes whose scopes the token holds; everything else answers 403, and calls go to a dedicated audit logger. A foreign key with a deletion policy ties tokens to the account. Second, a client settings page under user settings, Agent Access, to create (name, scopes, lifetime), copy the secret once, list with last use and revoke, shown only when the server reports the agent-access module feature. Third, agent-facing endpoints for competencies, announcements and courses, with summaries written for an agent; announcements got their own instructor-only endpoint because the general message endpoint stays closed to tokens. Fourth, exercises, lectures and programming exercises: read, create and change text, modeling and programming exercises, create lectures, add units with slides, read processing status. AgentDraftRules keeps token-created content unpublished by setting a future release date, allows changes only to still-unpublished content, and for programming exercises also requires no student participations; exam exercises are off limits. Fifth, a second OpenAPI file listing only token-reachable operations, generated with a Gradle task and published at /agent/openapi.yaml, with a test that fails when it drifts. Sixth, documentation and six agent skills in the layout Claude Code, Codex and Antigravity read, plus an llms.txt. Seventh, an MCP endpoint at /api/core/agent/mcp serving Streamable HTTP with 23 tools, each calling the same server's REST API over loopback with the caller's token so roles, scopes, validation and draft rules apply unchanged; posting announcements or creating lectures requires confirming the instructor approved the exact text. Eighth, user deletion and developer docs. Ninth, the agent module was written with records only, and the same convention was rolled out across the server, replacing JsonNode/ObjectNode/readTree/valueToTree with records, custom deserializers or the streaming API, enforced by a new JacksonTreeModelArchitectureTest.

How it went

The PR is large but internally consistent: integration tests cover the token flow and the MCP loopback client (including a test against a running server, which caught a real 401-instead-of-403 bug caused by servlet sendError dispatches losing the token), Vitest covers the settings page, and an architecture test enforces that opted-in endpoints keep role annotations, DTOs only and an entry in the agent spec. The OpenAPI drift test keeps the published agent spec honest. The author is explicit that data protection sign-off, an instructor pilot and a real-agent test against a test server have not happened, and that the PR exceeds the repo's one-functionality-per-PR guidance, offering the commit split as a review line.

Worth knowing

Feature is off by default (artemis.agent-access.enabled=false) and needs a server restart to enable. No per-token rate limit, no idempotency keys and no creation email; the existing limiter is IP-keyed. There is no persistent unpublished state, so the design relies on future release dates, meaning unreviewed content becomes visible on that date, and a lecture cannot be hidden at all today. Changing exercises students can already see is not available to tokens yet. Slides are uploaded via REST, not an MCP tool, because files do not fit tool calls. Tokens die with the owner's last credential change. No MCP SDK or Spring AI starter is used, so the endpoint is a hand-written stateless JSON-RPC handler.

Wiring

Enable with artemis.agent-access.enabled=true on the server. Tokens are created in user settings > Agent Access (name, scopes, lifetime up to 90 days), shown once, stored as SHA-256 hashes, and sent as Authorization: Bearer <token>. Endpoints must be annotated with @AllowedScopes and carry a role annotation; AllowedScopesArchitectureTest enforces role annotation, DTO-only payloads and an entry in the agent spec. The agent OpenAPI file is generated with ./gradlew generateOpenApiDocs -PopenApiGroup=agent pruneOpenApiSpec -x webapp and served at /agent/openapi.yaml; throwaway server ports move with -PopenApiServerPort and -PopenApiSshPort. The MCP endpoint at /api/core/agent/mcp is a stateless JSON-RPC handler over Streamable HTTP with 23 tools; each tool calls the same server's REST API over loopback with the caller's token, so scopes, roles, validation and AgentDraftRules apply unchanged. Announcement and lecture creation tools require an explicit confirmation that the instructor approved the exact text. Programming exercise code edits go through git with the existing personal VCS token, not this token. A JacksonTreeModelArchitectureTest bans JsonNode/ObjectNode/readTree/valueToTree in production code.

Try it yourself

Read the Artemis repo and the agent-access PR. Then, on a branch, implement the same shape for our app: (1) personal access tokens stored as SHA-256 hashes with scopes, expiry and revocation, authenticating as the owner and reaching only endpoints annotated with an allow-list annotation, with an architecture test that every opted-in endpoint keeps its role annotation and returns DTOs; (2) a settings page to create, copy once, list with last use and revoke tokens, gated on a server-reported feature flag; (3) a second OpenAPI spec listing only token-reachable operations, generated by a Gradle task and published at /agent/openapi.yaml, with a test that fails on drift; (4) an MCP endpoint over Streamable HTTP whose tools call our own REST API over loopback with the caller's token, so validation and authorization apply unchanged; (5) draft rules that keep token-created content unpublished via a future release date; (6) docs and agent skills in the layout Claude Code and Codex read, plus an llms.txt. Keep the feature off by default and list what you deliberately left out.

Source

Tools in this stack