repl: warn on stale MCP auto_approve keys (closes #33)
Auto-approve policy keys that point at unconnected aliases, mistyped tool names, or malformed forms were silently ignored — leaving the user with surprise confirm prompts and no diagnostic. validate_auto_approve() now walks config.mcp.auto_approve at startup (after the MCP connect loop) and after each :mcp connect. For each key: - "alias__*" — warn if alias has no live session - "alias__tool" — warn if alias unknown OR tool not in registry - anything else — warn as malformed (not in alias__tool form) Non-fatal. The re-run on :mcp connect lets a key that referenced a not-yet-connected alias become live without a restart. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -161,6 +161,48 @@ function M.run(config)
|
||||
return true, #sess:list_tools()
|
||||
end
|
||||
|
||||
-- Walk config.mcp.auto_approve and warn about keys that match no live
|
||||
-- tool / no live alias (issue #33). Stale entries silently failed to
|
||||
-- auto-approve, leaving the user with unexpected confirm prompts.
|
||||
-- Called at startup AND after :mcp connect so newly-arrived sessions
|
||||
-- retroactively validate any keys that referenced them.
|
||||
local function validate_auto_approve()
|
||||
local policy = config.mcp and config.mcp.auto_approve
|
||||
if not policy then return end
|
||||
for key, _ in pairs(policy) do
|
||||
local alias_glob = key:match("^(.-)__%*$")
|
||||
if alias_glob then
|
||||
if not mcp_sessions[alias_glob] then
|
||||
renderer.status(("auto_approve key '%s': no MCP server "
|
||||
.. "connected for alias '%s'"):format(key, alias_glob))
|
||||
end
|
||||
else
|
||||
local alias, tname = key:match("^(.-)__(.+)$")
|
||||
if not alias or alias == "" or not tname then
|
||||
renderer.status(("auto_approve key '%s': not in "
|
||||
.. "'alias__tool' or 'alias__*' form"):format(key))
|
||||
else
|
||||
local sess = mcp_sessions[alias]
|
||||
if not sess then
|
||||
renderer.status(("auto_approve key '%s': no MCP "
|
||||
.. "server connected for alias '%s'")
|
||||
:format(key, alias))
|
||||
else
|
||||
local found = false
|
||||
for _, t in ipairs(sess:list_tools()) do
|
||||
if t.name == tname then found = true; break end
|
||||
end
|
||||
if not found then
|
||||
renderer.status(("auto_approve key '%s': "
|
||||
.. "alias '%s' has no tool named '%s'")
|
||||
:format(key, alias, tname))
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
if config.mcp and config.mcp.servers then
|
||||
for alias, server_cfg in pairs(config.mcp.servers) do
|
||||
local ok, n = connect_mcp(alias, server_cfg)
|
||||
@@ -168,6 +210,7 @@ function M.run(config)
|
||||
renderer.status(("mcp %s: %d tools"):format(alias, n))
|
||||
end
|
||||
end
|
||||
validate_auto_approve()
|
||||
end
|
||||
|
||||
-- Assemble OpenAI-shape `tools` array across all live sessions, with
|
||||
@@ -827,6 +870,9 @@ function M.run(config)
|
||||
if ok then
|
||||
renderer.status(("mcp %s: connected (%d tools)")
|
||||
:format(alias, n))
|
||||
-- Re-validate auto_approve so any stale keys that
|
||||
-- referenced this alias become live (issue #33 bonus).
|
||||
validate_auto_approve()
|
||||
end
|
||||
elseif sub == "disconnect" then
|
||||
local alias = sub_args:match("^%s*(%S+)")
|
||||
|
||||
Reference in New Issue
Block a user