Files
computer-playbook/utils/roles/applications
kevinveenbirkenbachandClaude Opus 5 adacac7f29 fix(mcp): present the account's login in basic auth, not the users-dict key
The WordPress MCP endpoint answered 401 to every authenticated call, and
web-app-hermes and web-app-openclaw failed their CLI tests as cascades of the
same refusal. The WordPress container's own Apache log settles it: 121 POST,
100 GET and 21 HEAD on the MCP path, each with %u = mcp-web-app-wordpress and
status 401. A populated %u rules out a stripped Authorization header, and the
body is infinito_mcp_unauthorized, which only the mu-plugin's rest_pre_dispatch
guard emits - a wrong password for an existing user would surface WP core's
incorrect_password instead. So WordPress never resolved a user, because the
deploy sent the users-dict key while the login the deploy creates is
mcp-wordpress.

7916c0177e introduced the split when it capped usernames, deliberately keeping
the key because meta/mcp.yml's owner and the token store wire to it.
web-app-wordpress is the only role with auth: basic_auth - every other MCP
provider uses bearer, where the owner never travels - which is why only this
one broke.

resolve_credential now also returns the login, mcp_servers emits it on every
discovered entry, and the filter encodes login:token. It raises when no login
resolves rather than authenticating as the empty user, so the failure is loud
instead of another 401. The new mcp_client_login lookup shares
resolve_provider_credential with mcp_client_bearer so a second resolver cannot
drift from the first, and a test pins the two to the same function object.
test.env.j2 is the one place that hand-builds the server dict instead of
reading MCP_DISCOVERED_SERVERS, so it passes login as well.

The lint closes the class: auth: basic_auth requires credential.owner to be a
key some role's meta/users.yml declares. Without it a future basic-auth
provider sourced from credentials would hit the same fallback silently. It
imports BASIC_AUTHS from the filter so the rule and the renderer cannot drift
onto different spellings.

Tool names were verified against the adapter version the addon actually
installs, v0.7.0, not the v0.5.0 recorded in meta/mcp.yml: the sanitizer turns
the three registered abilities into infinito-get-post,
infinito-list-categories and infinito-search-posts, matching the rendered
allowlist exactly. Adding login to a discovered entry changes nothing for the
five consumers - three of them do not list basic_auth in supported_auths and
never see this server at all, and the other two read named keys only.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-10-16 12:37:30 +02:00
..
…
…
…

Application Utilities (utils/roles/applications/) 🧩

This package contains shared Python helpers that operate on the central applications configuration dictionary.

Purpose 🎯

Modules in utils/roles/applications/ MUST provide reusable helpers for reading, resolving, and validating application configuration. Helpers MUST stay independent of Ansible plugin entrypoints so the same code can be imported from filter plugins, lookup plugins, and library modules.

Example Import ✍️

from utils.roles.applications import config

value = config.get(
    applications,
    application_id,
    "features.oidc",
    strict=False,
    default=False,
)

Design Guidelines 📐

  • Helpers SHOULD be focused, composable, and free of side effects during import.
  • Helpers MUST NOT depend on Ansible plugin loader behavior at import time.
  • Helpers SHOULD raise typed exceptions (e.g. AppConfigKeyError) so callers can react to missing keys in strict mode.
  • Public functions SHOULD use concise names (e.g. get) and rely on module-qualified access at call sites (config.get(...)) for clarity.

What Does Not Belong Here 🚫

  • Standalone executable modules MUST live in library/.
  • Ansible plugin entrypoints MUST live under plugins/*.
  • Cross-cutting helpers that are not application-specific SHOULD live in the parent utils/ package instead.