Compact MCP write command results

This commit is contained in:
HumbleTurtle
2026-05-14 10:37:05 -03:00
parent 181aa8c42f
commit f320ea655f
3 changed files with 87 additions and 25 deletions
+71 -22
View File
@@ -5318,6 +5318,22 @@ def summarize_node(node, view="summary", preview_chars=240):
return {key: value for key, value in item.items() if value not in (None, "")} return {key: value for key, value in item.items() if value not in (None, "")}
def node_write_reference(node):
metadata = node_metadata(node)
item = {
"id": node.get("id"),
"path": node.get("full_path"),
"full_path": node.get("full_path"),
"type": node.get("type"),
"status": node_status(node),
"updated_at": node.get("updated_at"),
}
title = node.get("title") or metadata.get("title")
if title:
item["title"] = title
return {key: value for key, value in item.items() if value not in (None, "")}
def discovery_envelope(project_id, items, args, *, view=None, next_cursor=None, has_more=None, diagnostics=None, query_analysis=None, warnings=None): def discovery_envelope(project_id, items, args, *, view=None, next_cursor=None, has_more=None, diagnostics=None, query_analysis=None, warnings=None):
view = view or normalize_discovery_view(args)[0] view = view or normalize_discovery_view(args)[0]
limit = normalize_int(args.get("limit"), DISCOVERY_DEFAULT_LIMIT, 1, discovery_max_limit(view)) limit = normalize_int(args.get("limit"), DISCOVERY_DEFAULT_LIMIT, 1, discovery_max_limit(view))
@@ -7285,7 +7301,25 @@ def guarded_node_patch(args):
node = ((result.get("data") or {}).get("node") or {}) if isinstance(result, dict) else {} node = ((result.get("data") or {}).get("node") or {}) if isinstance(result, dict) else {}
if node: if node:
index_node(node) index_node(node)
return result if not isinstance(result, dict):
return {"ok": True, "changed": True, "path": path}
data = result.get("data") if isinstance(result.get("data"), dict) else {}
compact = {
"ok": bool(result.get("ok", True)),
"status": result.get("status") or ("dry_run" if args.get("dry_run") else "updated"),
"changed": not bool(data.get("dry_run")),
"dry_run": bool(data.get("dry_run")),
"path": node.get("full_path") or path,
}
if data.get("revision"):
compact["revision"] = data["revision"]
if data.get("read_fingerprint"):
compact["read_fingerprint"] = data["read_fingerprint"]
if result.get("warnings"):
compact["warnings"] = result["warnings"]
if result.get("diagnostics"):
compact["diagnostics"] = result["diagnostics"]
return compact
def try_get_node_by_path(path, allow_agent=False): def try_get_node_by_path(path, allow_agent=False):
@@ -7343,7 +7377,8 @@ def upsert_node(args):
existing["full_path"], existing["full_path"],
) )
index_node(updated) index_node(updated)
return {"created": False, "node": decrypt_node_for_return(updated, project_id=project_id)} node = decrypt_node_for_return(updated, project_id=project_id)
return {"ok": True, "changed": True, "created": False, "updated": True, **node_write_reference(node)}
write_path = normalize_node_path(args["path"]) write_path = normalize_node_path(args["path"])
created = request_project_write( created = request_project_write(
"POST", "POST",
@@ -7355,7 +7390,8 @@ def upsert_node(args):
write_path, write_path,
) )
index_node(created) index_node(created)
return {"created": True, "node": decrypt_node_for_return(created, project_id=project_id)} node = decrypt_node_for_return(created, project_id=project_id)
return {"ok": True, "changed": True, "created": True, **node_write_reference(node)}
def update_node_by_path(args): def update_node_by_path(args):
@@ -7372,7 +7408,8 @@ def update_node_by_path(args):
existing["full_path"], existing["full_path"],
) )
index_node(updated) index_node(updated)
return {"updated": True, "node": decrypt_node_for_return(updated, project_id=project_id)} node = decrypt_node_for_return(updated, project_id=project_id)
return {"ok": True, "changed": True, "updated": True, **node_write_reference(node)}
def archive_node_by_path(args): def archive_node_by_path(args):
@@ -7451,11 +7488,12 @@ def link_nodes(args):
updated = source updated = source
return { return {
"ok": True,
"changed": created,
"created": created, "created": created,
"relation": relation, "relation": relation,
"source": {"id": source["id"], "full_path": source["full_path"]}, "source": {"id": source["id"], "full_path": source["full_path"]},
"target": {"id": target["id"], "full_path": target["full_path"]}, "target": {"id": target["id"], "full_path": target["full_path"]},
"updated_node": updated,
} }
@@ -8171,9 +8209,15 @@ def create_kanban_card(args):
path, path,
) )
index_node(created) index_node(created)
result = {"created": True, "node": decrypt_node_for_return(created, project_id=project_id)} node = decrypt_node_for_return(created, project_id=project_id)
node = result.get("node", result) result = {"created": True, **node_write_reference(node)}
return {"ok": True, "created": bool(result.get("created", True)), "path": node.get("full_path"), "node": kanban_compact_card(node, view="summary")} return {
"ok": True,
"changed": True,
"created": bool(result.get("created", True)),
"path": result.get("path") or result.get("full_path"),
"column": column,
}
def update_kanban_card(args): def update_kanban_card(args):
@@ -8196,7 +8240,7 @@ def update_kanban_card(args):
if "content_text" in args or "body" in args: if "content_text" in args or "body" in args:
payload["content_text"] = args.get("content_text") if "content_text" in args else args.get("body") payload["content_text"] = args.get("content_text") if "content_text" in args else args.get("body")
updated = update_node_by_path(payload) updated = update_node_by_path(payload)
return {"ok": True, "updated": True, "node": kanban_compact_card(updated["node"], view="summary")} return {"ok": True, "changed": True, "updated": True, "path": updated.get("path") or existing["full_path"], "column": kanban_column(existing)}
def move_kanban_card(args, target_column): def move_kanban_card(args, target_column):
@@ -8224,7 +8268,7 @@ def move_kanban_card(args, target_column):
remove_indexed_node(existing["id"]) remove_indexed_node(existing["id"])
index_node(updated) index_node(updated)
node = decrypt_node_for_return(updated, project_id=project_id) node = decrypt_node_for_return(updated, project_id=project_id)
return {"ok": True, "changed": True, "from": source, "to": node.get("full_path"), "node": kanban_compact_card(node, view="summary")} return {"ok": True, "changed": True, "from": source, "to": node.get("full_path"), "path": node.get("full_path"), "column": target_column}
def block_kanban_card(args, blocked): def block_kanban_card(args, blocked):
@@ -8426,7 +8470,11 @@ def plan_lifecycle_update(args, *, action, terminal_status, required_target_key,
updated = node updated = node
if planned_changes != ["archive node"]: if planned_changes != ["archive node"]:
updated = update_node_by_path({"path": path, "metadata": metadata, "tags": tags, "links": links}).get("node") or updated update_node_by_path({"path": path, "metadata": metadata, "tags": tags, "links": links})
updated = dict(node)
updated["metadata"] = metadata
updated["tags"] = tags
updated["links"] = links
archive_result = archive_node_by_path({"path": path}) archive_result = archive_node_by_path({"path": path})
return plan_lifecycle_result( return plan_lifecycle_result(
action, action,
@@ -8839,7 +8887,6 @@ def move_node(args):
"paths": [updated["full_path"]], "paths": [updated["full_path"]],
"references_rewritten": references_to_rewrite, "references_rewritten": references_to_rewrite,
"links_rewritten": len(references_to_rewrite), "links_rewritten": len(references_to_rewrite),
"node": updated,
} }
@@ -8895,14 +8942,14 @@ def move_folder(args):
payload = {"folder_path": None if next_folder == "/" else next_folder} payload = {"folder_path": None if next_folder == "/" else next_folder}
saved = request_json("PATCH", f"/v1/projects/{project_id}/nodes/{node['id']}", payload, extra_headers=headers) saved = request_json("PATCH", f"/v1/projects/{project_id}/nodes/{node['id']}", payload, extra_headers=headers)
index_node(saved) index_node(saved)
updated.append(saved) updated.append(node_write_reference(saved))
finally: finally:
try: try:
release_project_lock({"lock_id": lock["id"], "lease_token": lock["lease_token"]}) release_project_lock({"lock_id": lock["id"], "lease_token": lock["lease_token"]})
except Exception: except Exception:
pass pass
links_rewritten = sum(len(item["references_to_rewrite"]) for item in reference_plan) links_rewritten = sum(len(item["references_to_rewrite"]) for item in reference_plan)
return {"ok": True, "changed": bool(updated), "dry_run": False, "moved": len(updated), "from_folder": from_folder, "to_folder": to_folder, "items": updated, "reference_plan": reference_plan, "links_rewritten": links_rewritten} return {"ok": True, "changed": bool(updated), "dry_run": False, "moved": len(updated), "from_folder": from_folder, "to_folder": to_folder, "paths": [item.get("path") for item in updated if item.get("path")], "items": updated, "reference_plan": reference_plan, "links_rewritten": links_rewritten}
def bulk_upsert_nodes(args): def bulk_upsert_nodes(args):
@@ -8932,13 +8979,14 @@ def bulk_upsert_nodes(args):
saved = request_json("POST", f"/v1/projects/{project_id}/nodes", payload, extra_headers=headers) saved = request_json("POST", f"/v1/projects/{project_id}/nodes", payload, extra_headers=headers)
created = True created = True
index_node(saved) index_node(saved)
results.append({"created": created, "node": decrypt_node_for_return(saved, project_id=project_id)}) node = decrypt_node_for_return(saved, project_id=project_id)
results.append({"created": created, **node_write_reference(node)})
finally: finally:
try: try:
release_project_lock({"lock_id": lock["id"], "lease_token": lock["lease_token"]}) release_project_lock({"lock_id": lock["id"], "lease_token": lock["lease_token"]})
except Exception: except Exception:
pass pass
return {"ok": True, "changed": bool(results), "dry_run": False, "count": len(results), "paths": [item["node"].get("full_path") for item in results], "items": results} return {"ok": True, "changed": bool(results), "dry_run": False, "count": len(results), "paths": [item.get("path") for item in results if item.get("path")], "items": results}
def agent_node_args(args, key="path"): def agent_node_args(args, key="path"):
@@ -8983,7 +9031,7 @@ def upsert_agent_node(args):
f"update agent node {existing['full_path']}", f"update agent node {existing['full_path']}",
) )
index_node(updated) index_node(updated)
return {"created": False, "node": updated} return {"ok": True, "changed": True, "created": False, "updated": True, **node_write_reference(updated)}
created = request_project_write( created = request_project_write(
"POST", "POST",
f"/v1/projects/{project_id}/nodes", f"/v1/projects/{project_id}/nodes",
@@ -8992,7 +9040,7 @@ def upsert_agent_node(args):
f"create agent node {scoped['path']}", f"create agent node {scoped['path']}",
) )
index_node(created) index_node(created)
return {"created": True, "node": created} return {"ok": True, "changed": True, "created": True, **node_write_reference(created)}
def update_agent_node(args): def update_agent_node(args):
@@ -9007,7 +9055,7 @@ def update_agent_node(args):
f"update agent node {existing['full_path']}", f"update agent node {existing['full_path']}",
) )
index_node(updated) index_node(updated)
return {"updated": True, "node": updated} return {"ok": True, "changed": True, "updated": True, **node_write_reference(updated)}
def archive_agent_node(args): def archive_agent_node(args):
@@ -9056,11 +9104,12 @@ def link_agent_node(args):
else: else:
updated = source updated = source
return { return {
"ok": True,
"changed": created,
"created": created, "created": created,
"relation": relation, "relation": relation,
"source": {"id": source["id"], "full_path": source["full_path"]}, "source": {"id": source["id"], "full_path": source["full_path"]},
"target": {"id": target["id"], "full_path": target["full_path"]}, "target": {"id": target["id"], "full_path": target["full_path"]},
"updated_node": updated,
} }
@@ -9079,8 +9128,8 @@ def create_agent(args):
"links": [], "links": [],
} }
) )
created.append(result["node"]) created.append(result)
return {"agent_slug": slug, "root_path": agent_root_path(slug), "nodes": created} return {"ok": True, "changed": bool(created), "agent_slug": slug, "root_path": agent_root_path(slug), "items": created}
def show_agent(args): def show_agent(args):
@@ -371,6 +371,7 @@ def test_kanban_workflow_create_move_and_block(mod):
created = mod.create_kanban_card({"title": "Add MCP commands", "priority": "HIGH"}) created = mod.create_kanban_card({"title": "Add MCP commands", "priority": "HIGH"})
assert created["created"] is True assert created["created"] is True
assert "node" not in created
assert writes[0][0] == "POST" assert writes[0][0] == "POST"
assert writes[0][2]["folder_path"] == "/kanban/todo" assert writes[0][2]["folder_path"] == "/kanban/todo"
assert writes[0][2]["metadata"]["priority"] == "high" assert writes[0][2]["metadata"]["priority"] == "high"
@@ -378,6 +379,7 @@ def test_kanban_workflow_create_move_and_block(mod):
moved = mod.move_kanban_card({"path": "/kanban/todo/add-mcp-commands"}, "doing") moved = mod.move_kanban_card({"path": "/kanban/todo/add-mcp-commands"}, "doing")
assert moved["to"] == "/kanban/doing/add-mcp-commands" assert moved["to"] == "/kanban/doing/add-mcp-commands"
assert "node" not in moved
assert writes[1][0] == "PATCH" assert writes[1][0] == "PATCH"
assert writes[1][2]["folder_path"] == "/kanban/doing" assert writes[1][2]["folder_path"] == "/kanban/doing"
assert writes[1][2]["metadata"]["column"] == "doing" assert writes[1][2]["metadata"]["column"] == "doing"
@@ -385,6 +387,7 @@ def test_kanban_workflow_create_move_and_block(mod):
blocked = mod.block_kanban_card({"path": "/kanban/todo/add-mcp-commands", "blocked_reason": "waiting"}, True) blocked = mod.block_kanban_card({"path": "/kanban/todo/add-mcp-commands", "blocked_reason": "waiting"}, True)
assert blocked["updated"] is True assert blocked["updated"] is True
assert "node" not in blocked
assert writes[2][2]["metadata"]["blocked"] is True assert writes[2][2]["metadata"]["blocked"] is True
assert writes[2][2]["metadata"]["blocked_by"] == "waiting" assert writes[2][2]["metadata"]["blocked_by"] == "waiting"
@@ -411,6 +414,7 @@ def test_kanban_create_deduplicates_overlapping_titles(mod):
created = mod.create_kanban_card({"title": "Add MCP commands", "column": "todo", "now": "2026-05-10T12:34:56Z"}) created = mod.create_kanban_card({"title": "Add MCP commands", "column": "todo", "now": "2026-05-10T12:34:56Z"})
assert created["path"] == "/kanban/todo/add-mcp-commands-20260510123456-2" assert created["path"] == "/kanban/todo/add-mcp-commands-20260510123456-2"
assert "node" not in created
assert writes[0][2]["name"] == "add-mcp-commands-20260510123456-2" assert writes[0][2]["name"] == "add-mcp-commands-20260510123456-2"
+12 -3
View File
@@ -350,7 +350,11 @@ def run():
mod.try_get_node_by_path = lambda path, allow_agent=False: None mod.try_get_node_by_path = lambda path, allow_agent=False: None
created = mod.upsert_node({"path": "/notes/beta", "content_text": "tee secret", "metadata": {"private": "tee"}}) created = mod.upsert_node({"path": "/notes/beta", "content_text": "tee secret", "metadata": {"private": "tee"}})
tee_body = writes[-1][2] tee_body = writes[-1][2]
assert created["node"]["content_text"] == "tee secret" assert created["ok"] is True
assert created["created"] is True
assert created["path"] == "/notes/beta"
assert "content_text" not in created
assert "metadata" not in created
assert tee_body["content_text"] == "" assert tee_body["content_text"] == ""
assert tee_body["metadata"] == {} assert tee_body["metadata"] == {}
assert tee_body["wrapping_epoch"] == 1 assert tee_body["wrapping_epoch"] == 1
@@ -363,7 +367,10 @@ def run():
writes.clear() writes.clear()
patched = mod.update_node_by_path({"path": "/notes/beta", "content_text": "patch secret"}) patched = mod.update_node_by_path({"path": "/notes/beta", "content_text": "patch secret"})
patch_body = writes[-1][2] patch_body = writes[-1][2]
assert patched["node"]["content_text"] == "patch secret" assert patched["ok"] is True
assert patched["updated"] is True
assert patched["path"] == "/notes/beta"
assert "content_text" not in patched
assert patch_body["encrypted_content_envelope"]["aad_subject"] == "node_id:node-1" assert patch_body["encrypted_content_envelope"]["aad_subject"] == "node_id:node-1"
bulk_writes = [] bulk_writes = []
@@ -386,7 +393,9 @@ def run():
"encrypted_metadata_envelope": payload.get("encrypted_metadata_envelope"), "encrypted_metadata_envelope": payload.get("encrypted_metadata_envelope"),
} }
bulk = mod.bulk_upsert_nodes({"nodes": [{"path": "/bulk/one", "content_text": "bulk secret"}]}) bulk = mod.bulk_upsert_nodes({"nodes": [{"path": "/bulk/one", "content_text": "bulk secret"}]})
assert bulk["items"][0]["node"]["content_text"] == "bulk secret" assert bulk["items"][0]["path"] == "/bulk/one"
assert "node" not in bulk["items"][0]
assert "content_text" not in bulk["items"][0]
assert bulk_writes[0][2]["content_text"] == "" assert bulk_writes[0][2]["content_text"] == ""
assert bulk_writes[0][2]["encrypted_content_envelope"]["aad_subject"].startswith("encryption_id:") assert bulk_writes[0][2]["encrypted_content_envelope"]["aad_subject"].startswith("encryption_id:")