Files
computer-playbook/roles/web-app-jenkins/files/mcp-api-token.groovy
kevinveenbirkenbachandClaude Opus 5 41efbeda11 feat(jenkins): serve MCP behind the controller's own credentials
Pin the MCP Server plugin and let a boot hook mint the API token MCP
clients present, then take that token over into sys-token-store and
verify it against the controller before the deploy continues.

Wait for the controller to answer before probing. A JCasC change restarts
Jenkins, and the probe carries no retries of its own, so a single 5xx
during startup failed the deploy with a credential that was valid all
along.

Authenticate with HTTP Basic. The plugin offers no bearer, which is why
basic_auth entered the service contract instead of the role working
around it.

Assert the guarded surface, not the handshake: the plugin answers
initialize anonymously because that call is capability negotiation and
carries no data, while an unauthenticated tools/list is refused and
leaks no tool inventory.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-02 22:18:02 +02:00

30 lines
866 B
Groovy

import hudson.model.User
import jenkins.security.ApiTokenProperty
def username = System.getenv('JENKINS_MCP_USERNAME')
def tokenName = System.getenv('JENKINS_MCP_TOKEN_NAME')
def tokenFile = System.getenv('JENKINS_MCP_TOKEN_FILE')
if (!username || !tokenName || !tokenFile) {
return
}
def target = new File(tokenFile)
def user = User.getById(username, true)
def store = user.getProperty(ApiTokenProperty.class).tokenStore
if (target.isFile() && store.findMatchingToken(target.text.trim()) != null) {
return
}
store.tokenListSortedByName.findAll { it.name == tokenName }.each { store.revokeToken(it.uuid) }
def minted = store.generateNewToken(tokenName)
user.save()
target.parentFile.mkdirs()
target.text = minted.plainValue
target.setReadable(false, false)
target.setWritable(false, false)
target.setReadable(true, true)
target.setWritable(true, true)