109 lines
4.8 KiB
PHP
109 lines
4.8 KiB
PHP
<?php
|
|
/**
|
|
* Batch 10 — Memory layer (Pro-equivalent, net-new).
|
|
*
|
|
* A small option/transient-backed key-value store so MCP tools can persist context
|
|
* across calls. Values are stored as strings (callers JSON-encode structured data).
|
|
* GPL-2.0-or-later. See NOTICE.md.
|
|
*
|
|
* Registered via the asktam_mcp_register_tools hook (glob auto-loader).
|
|
*/
|
|
|
|
namespace AskTamMCP\Extensions;
|
|
|
|
defined('ABSPATH') || exit;
|
|
|
|
class Batch10 {
|
|
|
|
private const PREFIX = 'ampmem_';
|
|
|
|
public static function register($registry): void {
|
|
$def = function (string $name, string $desc, array $props, array $required, string $scope, string $method) {
|
|
$schema = ['type' => 'object', 'properties' => $props];
|
|
if ($required) $schema['required'] = $required;
|
|
return ['name' => $name, 'description' => $desc, 'scope' => $scope,
|
|
'handler' => 'AskTamMCP\\Extensions\\Batch10::' . $method, 'inputSchema' => $schema];
|
|
};
|
|
|
|
$registry->register($def('memory_set', 'Store a string value under a key. Optional ttl_minutes makes it expire (otherwise it persists). For structured data, pass JSON text as value.',
|
|
['key' => ['type' => 'string'], 'value' => ['type' => 'string'], 'ttl_minutes' => ['type' => 'integer', 'default' => 0]],
|
|
['key', 'value'], 'write', 'setVal'));
|
|
|
|
$registry->register($def('memory_get', 'Retrieve a stored value by key.',
|
|
['key' => ['type' => 'string']], ['key'], 'read', 'getVal'));
|
|
|
|
$registry->register($def('memory_list', 'List stored memory keys, optionally filtered by prefix.',
|
|
['prefix' => ['type' => 'string']], [], 'read', 'listKeys'));
|
|
|
|
$registry->register($def('memory_delete', 'Delete a stored value by key.',
|
|
['key' => ['type' => 'string']], ['key'], 'write', 'deleteVal'));
|
|
|
|
$registry->register($def('memory_append', 'Append an item to a newline-delimited list stored at key (created if absent).',
|
|
['key' => ['type' => 'string'], 'item' => ['type' => 'string']], ['key', 'item'], 'write', 'appendVal'));
|
|
}
|
|
|
|
private static function key(string $raw): string {
|
|
$k = preg_replace('/[^a-z0-9_\-]/i', '', $raw);
|
|
if ($k === '') throw new \RuntimeException('Invalid key.');
|
|
return self::PREFIX . $k;
|
|
}
|
|
|
|
public static function setVal(array $args): array {
|
|
$opt = self::key((string) ($args['key'] ?? ''));
|
|
$val = (string) ($args['value'] ?? '');
|
|
$ttl = (int) ($args['ttl_minutes'] ?? 0);
|
|
if ($ttl > 0) { set_transient($opt, $val, $ttl * MINUTE_IN_SECONDS); delete_option($opt); }
|
|
else { update_option($opt, $val, false); delete_transient($opt); }
|
|
return ['key' => $args['key'], 'stored' => true, 'expires' => $ttl > 0 ? $ttl . 'm' : 'never'];
|
|
}
|
|
|
|
public static function getVal(array $args): array {
|
|
$opt = self::key((string) ($args['key'] ?? ''));
|
|
$v = get_option($opt, null);
|
|
$source = 'option';
|
|
if ($v === null) {
|
|
$t = get_transient($opt);
|
|
if ($t === false) { $v = null; $source = null; } else { $v = $t; $source = 'transient'; }
|
|
}
|
|
return ['key' => $args['key'], 'value' => $v, 'exists' => $v !== null, 'source' => $source];
|
|
}
|
|
|
|
public static function listKeys(array $args): array {
|
|
global $wpdb;
|
|
$prefix = isset($args['prefix']) ? (string) $args['prefix'] : '';
|
|
$like1 = self::PREFIX . '%';
|
|
$like2 = '_transient_' . self::PREFIX . '%';
|
|
$names = $wpdb->get_col($wpdb->prepare(
|
|
"SELECT option_name FROM {$wpdb->options} WHERE option_name LIKE %s OR option_name LIKE %s",
|
|
$like1, $like2
|
|
));
|
|
$keys = [];
|
|
foreach ((array) $names as $n) {
|
|
$k = str_replace(['_transient_timeout_', '_transient_', self::PREFIX], '', $n);
|
|
if ($k === '' || strpos($n, '_transient_timeout_') === 0) continue;
|
|
if ($prefix !== '' && strpos($k, $prefix) !== 0) continue;
|
|
$keys[$k] = true;
|
|
}
|
|
$out = array_keys($keys);
|
|
return ['count' => count($out), 'keys' => $out];
|
|
}
|
|
|
|
public static function deleteVal(array $args): array {
|
|
$opt = self::key((string) ($args['key'] ?? ''));
|
|
$a = delete_option($opt);
|
|
$b = delete_transient($opt);
|
|
return ['key' => $args['key'], 'deleted' => ($a || $b)];
|
|
}
|
|
|
|
public static function appendVal(array $args): array {
|
|
$opt = self::key((string) ($args['key'] ?? ''));
|
|
$item = (string) ($args['item'] ?? '');
|
|
$cur = get_option($opt, '');
|
|
if (!is_string($cur)) $cur = '';
|
|
$list = $cur === '' ? [] : explode("\n", $cur);
|
|
$list[] = $item;
|
|
update_option($opt, implode("\n", $list), false);
|
|
return ['key' => $args['key'], 'count' => count($list)];
|
|
}
|
|
}
|