feat: tools and limits settings tabs
Adds Einstellungen → LIT MCP → Tools (direct/indirect/disabled per ability, only ExposurePolicy-allowed modes offered) and → Limits (upload size, log retention, snapshot count), wired into Plugin.php. Ruling Q11b (carried from task 6): overview.md notes that direct visibility is configurable; hints in src/ that name a tool which is direct by default but can be switched to indirect (content-get, content-query, content-types, post-publish, media-search) now use the neutral "lit/<name> (direkt: lit-<name>)" form so they stay correct after an admin override. lit/guide (always direct) and the media-preview/-download DIRECT_ONLY tools are unaffected. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01At3V6SjwbMe6eMzVGW3Hs7
This commit is contained in:
@@ -0,0 +1,119 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace LIT\MCP\Admin;
|
||||
|
||||
use LIT\MCP\Abilities\ExposurePolicy;
|
||||
use LIT\MCP\Integrations\IntegrationRegistry;
|
||||
|
||||
/**
|
||||
* Tab "Tools": direct/indirect/disabled exposure per ability (spec §8.4). Only the modes
|
||||
* ExposurePolicy::isAllowed() allows for a given ability are offered; "Standard" saves nothing,
|
||||
* so a later version's changed default takes effect automatically.
|
||||
*/
|
||||
final class ToolsTab implements AdminTab
|
||||
{
|
||||
public const GROUP = 'lit_mcp_tools';
|
||||
|
||||
public function __construct(private IntegrationRegistry $registry, private ExposurePolicy $policy)
|
||||
{
|
||||
}
|
||||
|
||||
public function slug(): string
|
||||
{
|
||||
return 'tools';
|
||||
}
|
||||
|
||||
public function label(): string
|
||||
{
|
||||
return __('Tools', 'wp-lit-mcp');
|
||||
}
|
||||
|
||||
public function registerSettings(): void
|
||||
{
|
||||
register_setting(self::GROUP, ExposurePolicy::OPTION, [
|
||||
'type' => 'array',
|
||||
'sanitize_callback' => [ExposurePolicy::class, 'sanitize'],
|
||||
'default' => [],
|
||||
]);
|
||||
}
|
||||
|
||||
public function registerActions(): void
|
||||
{
|
||||
}
|
||||
|
||||
public function render(): void
|
||||
{
|
||||
echo '<p>'
|
||||
. esc_html__('Direkt: sofort als eigenes Tool sichtbar.', 'wp-lit-mcp') . ' '
|
||||
. esc_html__('Indirekt: nur über die Meta-Tools (discover/execute) erreichbar.', 'wp-lit-mcp') . ' '
|
||||
. esc_html__('Deaktiviert: gar nicht verfügbar.', 'wp-lit-mcp')
|
||||
. '</p>';
|
||||
echo '<p>'
|
||||
. esc_html__(
|
||||
'Die Server-Anweisungen beim Verbinden nennen immer die aktuell direkt sichtbaren Tools.',
|
||||
'wp-lit-mcp'
|
||||
) . ' '
|
||||
. esc_html__(
|
||||
'Änderungen gelten also ab der nächsten Verbindung; in claude.ai ggf. den Connector neu verbinden.',
|
||||
'wp-lit-mcp'
|
||||
)
|
||||
. '</p>';
|
||||
echo '<p>' . esc_html__(
|
||||
'Das Divi-Code-Modul ist immer erlaubt (nur nachrangig) und hat keine eigene Einstellung.',
|
||||
'wp-lit-mcp'
|
||||
) . '</p>';
|
||||
echo '<form method="post" action="options.php">';
|
||||
settings_fields(self::GROUP);
|
||||
echo '<table class="widefat striped"><thead><tr><th>' . esc_html__('Tool', 'wp-lit-mcp') . '</th><th>'
|
||||
. esc_html__('Einstellung', 'wp-lit-mcp') . '</th></tr></thead><tbody>';
|
||||
|
||||
foreach ($this->registry->abilities() as $ability) {
|
||||
$name = $ability->name();
|
||||
$current = $this->policy->mode($ability);
|
||||
$isDefault = $current === $this->policy->defaultMode($ability);
|
||||
printf(
|
||||
'<tr><td><strong>%s</strong><br><code>%s</code></td><td><select name="%s[%s]">',
|
||||
esc_html($ability->label()),
|
||||
esc_html($name),
|
||||
esc_attr(ExposurePolicy::OPTION),
|
||||
esc_attr($name)
|
||||
);
|
||||
printf(
|
||||
'<option value=""%s>%s</option>',
|
||||
selected($isDefault, true, false),
|
||||
esc_html(sprintf(
|
||||
/* translators: %s: default mode */
|
||||
__('Standard (%s)', 'wp-lit-mcp'),
|
||||
$this->modeLabel($this->policy->defaultMode($ability))
|
||||
))
|
||||
);
|
||||
foreach (ExposurePolicy::MODES as $mode) {
|
||||
if (! ExposurePolicy::isAllowed($name, $mode)) {
|
||||
continue;
|
||||
}
|
||||
printf(
|
||||
'<option value="%s"%s>%s</option>',
|
||||
esc_attr($mode),
|
||||
selected(! $isDefault && $current === $mode, true, false),
|
||||
esc_html($this->modeLabel($mode))
|
||||
);
|
||||
}
|
||||
echo '</select></td></tr>';
|
||||
}
|
||||
|
||||
echo '</tbody></table>';
|
||||
submit_button();
|
||||
echo '</form>';
|
||||
}
|
||||
|
||||
private function modeLabel(string $mode): string
|
||||
{
|
||||
return match ($mode) {
|
||||
ExposurePolicy::DIRECT => __('direkt', 'wp-lit-mcp'),
|
||||
ExposurePolicy::INDIRECT => __('indirekt', 'wp-lit-mcp'),
|
||||
default => __('deaktiviert', 'wp-lit-mcp'),
|
||||
};
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user