Skip to main content
Glama

zk_remove_link

Delete a link between two notes in a Zettelkasten system by specifying source and target note IDs, optionally removing bidirectional connections.

Instructions

Remove a link between two notes. Args: source_id: ID of the source note target_id: ID of the target note bidirectional: Whether to remove the link in both directions

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
bidirectionalNo
source_idYes
target_idYes

Implementation Reference

  • MCP tool decorator and handler function implementing zk_remove_link. Handles input parameters, calls ZettelService.remove_link, and returns success/error messages.
    @self.mcp.tool(
        name="zk_remove_link",
        description="Remove an existing link between two notes.",
        annotations={
            "readOnlyHint": False,
            "destructiveHint": True,
            "idempotentHint": True,
        },
    )
    def zk_remove_link(
        source_id: str, target_id: str, bidirectional: bool = False
    ) -> str:
        """Remove an existing link between two notes.
    
        Args:
            source_id: The unique ID of the source note
            target_id: The unique ID of the target note
            bidirectional: If true, removes links in both directions (source→target and target→source)
        """
        try:
            # Remove the link
            source_note, target_note = self.zettel_service.remove_link(
                source_id=str(source_id),
                target_id=str(target_id),
                bidirectional=bidirectional,
            )
            if bidirectional:
                return f"Bidirectional link removed between {source_id} and {target_id}"
            else:
                return f"Link removed from {source_id} to {target_id}"
        except Exception as e:
            return self.format_error_response(e)
    
    logger.debug("Tool zk_remove_link registered")
  • ZettelService.remove_link method called by the tool handler. Retrieves notes, calls model remove_link on source (and target if bidirectional), and persists changes.
    def remove_link(
        self,
        source_id: str,
        target_id: str,
        link_type: Optional[LinkType] = None,
        bidirectional: bool = False
    ) -> Tuple[Note, Optional[Note]]:
        """Remove a link between notes."""
        source_note = self.repository.get(source_id)
        if not source_note:
            raise ValueError(f"Source note with ID {source_id} not found")
        
        # Remove link from source to target
        source_note.remove_link(target_id, link_type)
        source_note = self.repository.update(source_note)
        
        # If bidirectional, remove link from target to source
        reverse_note = None
        if bidirectional:
            target_note = self.repository.get(target_id)
            if target_note:
                target_note.remove_link(source_id, link_type)
                reverse_note = self.repository.update(target_note)
        
        return source_note, reverse_note
  • Note model method that implements the core link removal logic by filtering the links list and updating timestamp.
    def remove_link(self, target_id: str, link_type: Optional[LinkType] = None) -> None:
        """Remove a link to another note."""
        if link_type:
            self.links = [
                link for link in self.links 
                if not (link.target_id == target_id and link.link_type == link_type)
            ]
        else:
            self.links = [link for link in self.links if link.target_id != target_id]
        self.updated_at = datetime.datetime.now()
  • Enum defining possible link types used in remove_link operations.
    class LinkType(str, Enum):
        """Types of links between notes."""
        REFERENCE = "reference"        # Simple reference to another note
        EXTENDS = "extends"            # Current note extends another note
        EXTENDED_BY = "extended_by"    # Current note is extended by another note
        REFINES = "refines"            # Current note refines another note
        REFINED_BY = "refined_by"      # Current note is refined by another note
        CONTRADICTS = "contradicts"    # Current note contradicts another note
        CONTRADICTED_BY = "contradicted_by"  # Current note is contradicted by another note
        QUESTIONS = "questions"        # Current note questions another note
        QUESTIONED_BY = "questioned_by"  # Current note is questioned by another note
        SUPPORTS = "supports"          # Current note supports another note
        SUPPORTED_BY = "supported_by"  # Current note is supported by another note
        RELATED = "related"            # Notes are related in some way
Behavior2/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

With no annotations provided, the description carries the full burden of behavioral disclosure. It states the action ('Remove a link') but lacks critical details: whether this operation is reversible, if it requires specific permissions, what happens if the link doesn't exist (e.g., error or no-op), or any side effects on note metadata. For a mutation tool with zero annotation coverage, this is a significant gap in transparency.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is front-loaded with the core purpose in the first sentence, followed by a structured Args section. It avoids unnecessary fluff, but the Args formatting adds slight verbosity. Overall, it's efficient and well-organized, with each sentence contributing directly to understanding.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness3/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

Given the tool's moderate complexity (mutation with 3 parameters), no annotations, and no output schema, the description is partially complete. It covers parameter meanings adequately but lacks behavioral context (e.g., error handling, effects) and output expectations. For a link removal operation, this leaves gaps that could hinder correct agent invocation.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

The description adds meaningful semantics for all three parameters beyond the schema, which has 0% description coverage. It explains that 'source_id' and 'target_id' are note IDs for the link endpoints, and clarifies 'bidirectional' as controlling removal direction. This compensates well for the schema's lack of descriptions, though it could note format expectations for IDs.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose4/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description clearly states the action ('Remove a link') and resource ('between two notes'), which is specific and unambiguous. However, it doesn't explicitly differentiate from sibling tools like 'zk_delete_note' or 'zk_create_link' beyond the obvious verb difference, missing an opportunity to clarify its unique role in link management versus note deletion.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines2/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description provides no guidance on when to use this tool versus alternatives. It doesn't mention prerequisites (e.g., that a link must exist), contrast with 'zk_create_link' for adding links, or explain scenarios where removal is appropriate (e.g., cleaning up outdated connections). This leaves the agent without contextual usage cues.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

Install Server

Other Tools

Related Tools

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/Liam-Deacon/zettelkasten-mcp'

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