Skip to main content
Glama

move_node_relative

Reposition outline nodes by moving a selected node and its children to a new location relative to another node, enabling intuitive reorganization of document structure.

Instructions

Move a node (and all its children) to a new position relative to a reference node. This is the intuitive way to reorganize your outline - just specify where you want the node to go.

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
source_urlYesURL of the node to move (with deep link #z=nodeId). The entire subtree (node + all descendants) will be moved.
reference_urlYesURL of the reference node that determines the target location
positionYesWhere to place the node relative to the reference: 'after' = immediately after the reference (same parent, same level), 'before' = immediately before the reference (same parent, same level), 'as_first_child' = as the first child inside the reference node, 'as_last_child' = as the last child inside the reference node

Implementation Reference

  • The core handler function implementing the move_node_relative tool: parses URLs, validates same document, computes target parent/index based on position (using findNodeParent helper), performs move via client.editDocument, returns success message with new URL.
    async ({ source_url, reference_url, position }) => { // Parse URLs const sourceParsed = parseDynalistUrl(source_url); const refParsed = parseDynalistUrl(reference_url); if (!sourceParsed.nodeId) { return { content: [{ type: "text", text: "Error: source_url must include a node deep link (#z=nodeId)" }], isError: true, }; } if (!refParsed.nodeId) { return { content: [{ type: "text", text: "Error: reference_url must include a node deep link (#z=nodeId)" }], isError: true, }; } if (sourceParsed.documentId !== refParsed.documentId) { return { content: [{ type: "text", text: "Error: Both nodes must be in the same document" }], isError: true, }; } const documentId = sourceParsed.documentId; const sourceNodeId = sourceParsed.nodeId; const refNodeId = refParsed.nodeId; // Fetch document to find parent/index const doc = await client.readDocument(documentId); let targetParentId: string; let targetIndex: number; if (position === "as_first_child") { targetParentId = refNodeId; targetIndex = 0; } else if (position === "as_last_child") { targetParentId = refNodeId; targetIndex = -1; } else { // "after" or "before" - find the parent of the reference node const refParentInfo = findNodeParent(doc.nodes, refNodeId); if (!refParentInfo) { return { content: [{ type: "text", text: "Error: Could not find parent of reference node" }], isError: true, }; } targetParentId = refParentInfo.parentId; targetIndex = position === "after" ? refParentInfo.index + 1 : refParentInfo.index; } // Execute move await client.editDocument(documentId, [ { action: "move", node_id: sourceNodeId, parent_id: targetParentId, index: targetIndex } ]); return { content: [ { type: "text", text: `Node moved successfully!\nSource: ${sourceNodeId}\nPosition: ${position} reference node\nNew Parent: ${targetParentId}\nNew URL: ${buildDynalistUrl(documentId, sourceNodeId)}`, }, ], }; }
  • Zod input schema defining parameters: source_url (string), reference_url (string), position (enum: after, before, as_first_child, as_last_child) with detailed descriptions.
    { source_url: z.string().describe("URL of the node to move (with deep link #z=nodeId). The entire subtree (node + all descendants) will be moved."), reference_url: z.string().describe("URL of the reference node that determines the target location"), position: z.enum(["after", "before", "as_first_child", "as_last_child"]).describe( "Where to place the node relative to the reference: " + "'after' = immediately after the reference (same parent, same level), " + "'before' = immediately before the reference (same parent, same level), " + "'as_first_child' = as the first child inside the reference node, " + "'as_last_child' = as the last child inside the reference node" ), },
  • MCP server.tool registration for 'move_node_relative' including name, description, schema, and inline handler function.
    server.tool( "move_node_relative", "Move a node (and all its children) to a new position relative to a reference node. This is the intuitive way to reorganize your outline - just specify where you want the node to go.", { source_url: z.string().describe("URL of the node to move (with deep link #z=nodeId). The entire subtree (node + all descendants) will be moved."), reference_url: z.string().describe("URL of the reference node that determines the target location"), position: z.enum(["after", "before", "as_first_child", "as_last_child"]).describe( "Where to place the node relative to the reference: " + "'after' = immediately after the reference (same parent, same level), " + "'before' = immediately before the reference (same parent, same level), " + "'as_first_child' = as the first child inside the reference node, " + "'as_last_child' = as the last child inside the reference node" ), }, async ({ source_url, reference_url, position }) => { // Parse URLs const sourceParsed = parseDynalistUrl(source_url); const refParsed = parseDynalistUrl(reference_url); if (!sourceParsed.nodeId) { return { content: [{ type: "text", text: "Error: source_url must include a node deep link (#z=nodeId)" }], isError: true, }; } if (!refParsed.nodeId) { return { content: [{ type: "text", text: "Error: reference_url must include a node deep link (#z=nodeId)" }], isError: true, }; } if (sourceParsed.documentId !== refParsed.documentId) { return { content: [{ type: "text", text: "Error: Both nodes must be in the same document" }], isError: true, }; } const documentId = sourceParsed.documentId; const sourceNodeId = sourceParsed.nodeId; const refNodeId = refParsed.nodeId; // Fetch document to find parent/index const doc = await client.readDocument(documentId); let targetParentId: string; let targetIndex: number; if (position === "as_first_child") { targetParentId = refNodeId; targetIndex = 0; } else if (position === "as_last_child") { targetParentId = refNodeId; targetIndex = -1; } else { // "after" or "before" - find the parent of the reference node const refParentInfo = findNodeParent(doc.nodes, refNodeId); if (!refParentInfo) { return { content: [{ type: "text", text: "Error: Could not find parent of reference node" }], isError: true, }; } targetParentId = refParentInfo.parentId; targetIndex = position === "after" ? refParentInfo.index + 1 : refParentInfo.index; } // Execute move await client.editDocument(documentId, [ { action: "move", node_id: sourceNodeId, parent_id: targetParentId, index: targetIndex } ]); return { content: [ { type: "text", text: `Node moved successfully!\nSource: ${sourceNodeId}\nPosition: ${position} reference node\nNew Parent: ${targetParentId}\nNew URL: ${buildDynalistUrl(documentId, sourceNodeId)}`, }, ], }; } );

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/cristip73/dynalist-mcp'

If you have feedback or need assistance with the MCP directory API, please join our Discord server