feat(changelog): Hook lit_mcp_changed
ChangeLog::record() löst nach jeder erfolgreichen oder teilweise gespeicherten Änderung lit_mcp_changed(ability, objectType, objectId, logId) aus, nie bei abgelehnten. Die Werte sind gekürzt wie im Protokoll; Ausnahmen von Listenern werden protokolliert und ändern das Ergebnis des Tools nicht. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5.5
parent
1a294b7881
commit
c383b99e7d
@@ -18,6 +18,9 @@ class ChangeLog
|
||||
{
|
||||
public const TABLE = 'lit_mcp_log';
|
||||
|
||||
/** Fires after every write that changed something (result ok or partial), see announce(). */
|
||||
public const CHANGED_ACTION = 'lit_mcp_changed';
|
||||
|
||||
public function __construct(private ?\wpdb $db = null)
|
||||
{
|
||||
}
|
||||
@@ -42,15 +45,19 @@ class ChangeLog
|
||||
string $result = 'ok'
|
||||
): ?int {
|
||||
$wpdb = $this->db();
|
||||
// Shortened once: the row and the announcement carry the same values.
|
||||
$ability = mb_substr($ability, 0, 100);
|
||||
$objectType = mb_substr($objectType, 0, 50);
|
||||
$objectId = mb_substr($objectId, 0, 191);
|
||||
$inserted = $wpdb->insert(
|
||||
$this->tableName(),
|
||||
[
|
||||
'created_at' => current_time('mysql', true),
|
||||
'user_id' => get_current_user_id(),
|
||||
'client' => $this->sanitizedClientName(),
|
||||
'ability' => mb_substr($ability, 0, 100),
|
||||
'object_type' => mb_substr($objectType, 0, 50),
|
||||
'object_id' => mb_substr($objectId, 0, 191),
|
||||
'ability' => $ability,
|
||||
'object_type' => $objectType,
|
||||
'object_id' => $objectId,
|
||||
'summary' => mb_substr($summary, 0, 500),
|
||||
'revision_id' => $revisionId,
|
||||
'snapshot_id' => $snapshotId,
|
||||
@@ -59,12 +66,36 @@ class ChangeLog
|
||||
['%s', '%d', '%s', '%s', '%s', '%s', '%s', '%d', '%s', '%s']
|
||||
);
|
||||
|
||||
$logId = null;
|
||||
if ($inserted === false) {
|
||||
Log::line(sprintf('change log insert failed for %s %s:%s', $ability, $objectType, $objectId));
|
||||
return null;
|
||||
} else {
|
||||
$logId = (int) $wpdb->insert_id;
|
||||
}
|
||||
|
||||
return (int) $wpdb->insert_id;
|
||||
if ($result !== 'refused') {
|
||||
self::announce($ability, $objectType, $objectId, $logId);
|
||||
}
|
||||
|
||||
return $logId;
|
||||
}
|
||||
|
||||
/**
|
||||
* do_action('lit_mcp_changed', ability, objectType, objectId, logId): a write happened (result ok or partial,
|
||||
* never refused), even if its log row did not (logId null).
|
||||
*
|
||||
* ability, objectType and objectId are shortened like the log row (100/50/191 characters). objectId is an
|
||||
* identifier or name from the tool input (post ID, option name, menu location ...): untrusted text, validate
|
||||
* before use. A listener that throws is caught and logged, the caller must not see a failed write; exit and
|
||||
* wp_die in a listener are not caught and end the request.
|
||||
*/
|
||||
private static function announce(string $ability, string $objectType, string $objectId, ?int $logId): void
|
||||
{
|
||||
try {
|
||||
do_action(self::CHANGED_ACTION, $ability, $objectType, $objectId, $logId);
|
||||
} catch (\Throwable $e) {
|
||||
Log::failure(self::CHANGED_ACTION, $e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
Reference in New Issue
Block a user