Files
computer-playbook/roles/web-app-zammad/files/ruby/apply/mcp_bot.rb
kevinveenbirkenbachandClaude Opus 5 31fa1e5540 docs(zammad): state why the ruby apply scripts have no unit mirror
The lint wants either a mirrored unit test per role script or a
nocheck: mirrored-unit-test directive with a reason.

ai_provider.rb runs in the Zammad console and writes through Setting, whose
validator pings the gateway on save, so the round trip against the live
provider is the entire contract; a mocked transport would assert the mock.
mcp_bot.rb works against User, Role and Token and depends on the
before_create hook the live models supply, none of which is callable outside
the Zammad image.

There is no tests/unit/ruby tree for such a mirror to live in.

The first reason is confirmed by this session: writing the AI provider
setting is itself the reachability proof, because Setting.set pings.

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

51 lines
1.9 KiB
Ruby

# nocheck: mirrored-unit-test - runs in the Zammad console against User, Role and Token,
# and depends on the before_create hook the live models supply; none of it is callable
# outside the Zammad image
#
# Give the MCP adapter a read-only Zammad agent and pin its API token.
#
# Zammad overwrites any token handed to create with a generated one, but only
# in before_create, so the chosen value is assigned afterwards. The column is
# plain text and the model renews it the same way.
#
# Read-only comes from the group access level, not from the role: an agent with
# `read` on its groups passes the show policies and fails the create/change
# ones. The Agent role itself is required, because ticket and organization
# reads are gated on `ticket.agent`.
#
# Environment:
# MCP_BOT_LOGIN, MCP_BOT_EMAIL, MCP_BOT_FIRSTNAME, MCP_BOT_LASTNAME,
# MCP_BOT_PASSWORD: the agent to converge.
# MCP_API_TOKEN: the token value the adapter presents.
# MCP_TOKEN_LABEL: the token's human label.
UserInfo.current_user_id = 1
Setting.set('api_token_access', true)
bot = User.find_or_initialize_by(login: ENV.fetch('MCP_BOT_LOGIN'))
bot.email = ENV.fetch('MCP_BOT_EMAIL')
bot.firstname = ENV.fetch('MCP_BOT_FIRSTNAME')
bot.lastname = ENV.fetch('MCP_BOT_LASTNAME')
bot.password = ENV.fetch('MCP_BOT_PASSWORD')
bot.active = true
bot.roles = Role.where(name: %w[Agent])
bot.save!
group = Group.find_or_create_by!(name: 'Users') { |g| g.active = true }
bot.group_names_access_map = { group.name => %w[read] }
bot.save!
label = ENV.fetch('MCP_TOKEN_LABEL')
token = Token.find_by(action: 'api', user_id: bot.id, name: label)
token ||= Token.create!(
action: 'api',
persistent: true,
user: bot,
name: label,
preferences: { permission: %w[ticket.agent] }
)
token.preferences = { permission: %w[ticket.agent] }
token.token = ENV.fetch('MCP_API_TOKEN')
token.save!