Skip to main content
Glama
bazylhorsey
by bazylhorsey

suggest_links

Find relevant notes to link within your Obsidian vault by analyzing content relationships and suggesting connections based on your current note's context.

Instructions

Suggest related notes for linking

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
limitNoMaximum suggestions (default: 5)
pathYesPath to the note
vaultYesVault name

Implementation Reference

  • MCP tool handler for 'suggest_links': retrieves all notes from the vault, updates the knowledge graph service, calls suggestRelatedNotes to get suggestions, and returns them as JSON.
    case 'suggest_links': {
      const connector = this.connectors.get(args?.vault as string);
      if (!connector) {
        throw new Error(`Vault "${args?.vault}" not found`);
      }
      const notesResult = await connector.getAllNotes();
      if (notesResult.success && notesResult.data) {
        this.knowledgeGraph.updateNotes(notesResult.data);
        const suggestions = this.knowledgeGraph.suggestRelatedNotes(args?.path as string, (args?.limit as number) || 5);
        return {
          content: [{ type: 'text', text: JSON.stringify(suggestions, null, 2) }],
        };
      }
      throw new Error('Failed to suggest links');
    }
  • Input schema definition for the 'suggest_links' tool, specifying required vault and path, optional limit.
      name: 'suggest_links',
      description: 'Suggest related notes for linking',
      inputSchema: {
        type: 'object',
        properties: {
          vault: { type: 'string', description: 'Vault name' },
          path: { type: 'string', description: 'Path to the note' },
          limit: { type: 'number', description: 'Maximum suggestions (default: 5)' },
        },
        required: ['vault', 'path'],
      },
    },
  • Core implementation of related note suggestions: computes similarity scores based on shared tags (weight 10), common title words >3 chars (weight 5), same folder (weight 3), sorts and limits results.
    suggestRelatedNotes(notePath: string, limit: number = 5): Array<{ path: string; score: number }> {
      const note = this.notes.get(notePath);
      if (!note) return [];
    
      const scores = new Map<string, number>();
    
      // Score based on shared tags
      if (note.tags) {
        for (const otherNote of this.notes.values()) {
          if (otherNote.path === notePath) continue;
    
          if (otherNote.tags) {
            const sharedTags = note.tags.filter(tag => otherNote.tags!.includes(tag));
            if (sharedTags.length > 0) {
              scores.set(otherNote.path, (scores.get(otherNote.path) || 0) + sharedTags.length * 10);
            }
          }
        }
      }
    
      // Score based on common words in title
      const noteWords = new Set(note.title.toLowerCase().split(/\s+/));
      for (const otherNote of this.notes.values()) {
        if (otherNote.path === notePath) continue;
    
        const otherWords = otherNote.title.toLowerCase().split(/\s+/);
        const commonWords = otherWords.filter(word => noteWords.has(word) && word.length > 3);
    
        if (commonWords.length > 0) {
          scores.set(otherNote.path, (scores.get(otherNote.path) || 0) + commonWords.length * 5);
        }
      }
    
      // Score based on folder proximity
      const folder = this.getFolder(notePath);
      for (const otherNote of this.notes.values()) {
        if (otherNote.path === notePath) continue;
    
        if (this.getFolder(otherNote.path) === folder) {
          scores.set(otherNote.path, (scores.get(otherNote.path) || 0) + 3);
        }
      }
    
      return Array.from(scores.entries())
        .map(([path, score]) => ({ path, score }))
        .sort((a, b) => b.score - a.score)
        .slice(0, limit);
    }
  • src/index.ts:64-474 (registration)
    Registers all tools including 'suggest_links' by returning the tools list in ListToolsRequestSchema handler.
    this.server.setRequestHandler(ListToolsRequestSchema, async () => ({
      tools: [
        {
          name: 'get_note',
          description: 'Get a note by its path',
          inputSchema: {
            type: 'object',
            properties: {
              vault: { type: 'string', description: 'Vault name' },
              path: { type: 'string', description: 'Path to the note' },
            },
            required: ['vault', 'path'],
          },
        },
        {
          name: 'search_notes',
          description: 'Search for notes in a vault',
          inputSchema: {
            type: 'object',
            properties: {
              vault: { type: 'string', description: 'Vault name' },
              query: { type: 'string', description: 'Search query' },
              tags: { type: 'array', items: { type: 'string' }, description: 'Filter by tags' },
              folder: { type: 'string', description: 'Filter by folder' },
              limit: { type: 'number', description: 'Maximum number of results' },
            },
            required: ['vault', 'query'],
          },
        },
        {
          name: 'create_note',
          description: 'Create a new note',
          inputSchema: {
            type: 'object',
            properties: {
              vault: { type: 'string', description: 'Vault name' },
              path: { type: 'string', description: 'Path for the new note' },
              content: { type: 'string', description: 'Note content' },
              frontmatter: { type: 'object', description: 'Frontmatter metadata' },
            },
            required: ['vault', 'path', 'content'],
          },
        },
        {
          name: 'update_note',
          description: 'Update an existing note',
          inputSchema: {
            type: 'object',
            properties: {
              vault: { type: 'string', description: 'Vault name' },
              path: { type: 'string', description: 'Path to the note' },
              content: { type: 'string', description: 'New content' },
              frontmatter: { type: 'object', description: 'Updated frontmatter' },
            },
            required: ['vault', 'path', 'content'],
          },
        },
        {
          name: 'delete_note',
          description: 'Delete a note',
          inputSchema: {
            type: 'object',
            properties: {
              vault: { type: 'string', description: 'Vault name' },
              path: { type: 'string', description: 'Path to the note' },
            },
            required: ['vault', 'path'],
          },
        },
        {
          name: 'get_vault_stats',
          description: 'Get statistics about a vault',
          inputSchema: {
            type: 'object',
            properties: {
              vault: { type: 'string', description: 'Vault name' },
            },
            required: ['vault'],
          },
        },
        {
          name: 'list_tags',
          description: 'List all tags in a vault',
          inputSchema: {
            type: 'object',
            properties: {
              vault: { type: 'string', description: 'Vault name' },
            },
            required: ['vault'],
          },
        },
        {
          name: 'list_folders',
          description: 'List all folders in a vault',
          inputSchema: {
            type: 'object',
            properties: {
              vault: { type: 'string', description: 'Vault name' },
            },
            required: ['vault'],
          },
        },
        {
          name: 'get_knowledge_graph',
          description: 'Get the complete knowledge graph for a vault',
          inputSchema: {
            type: 'object',
            properties: {
              vault: { type: 'string', description: 'Vault name' },
            },
            required: ['vault'],
          },
        },
        {
          name: 'get_related_notes',
          description: 'Get notes related to a specific note',
          inputSchema: {
            type: 'object',
            properties: {
              vault: { type: 'string', description: 'Vault name' },
              path: { type: 'string', description: 'Path to the note' },
              maxDepth: { type: 'number', description: 'Maximum link depth (default: 2)' },
            },
            required: ['vault', 'path'],
          },
        },
        {
          name: 'analyze_graph',
          description: 'Analyze the knowledge graph structure',
          inputSchema: {
            type: 'object',
            properties: {
              vault: { type: 'string', description: 'Vault name' },
            },
            required: ['vault'],
          },
        },
        {
          name: 'suggest_links',
          description: 'Suggest related notes for linking',
          inputSchema: {
            type: 'object',
            properties: {
              vault: { type: 'string', description: 'Vault name' },
              path: { type: 'string', description: 'Path to the note' },
              limit: { type: 'number', description: 'Maximum suggestions (default: 5)' },
            },
            required: ['vault', 'path'],
          },
        },
        // Canvas tools
        {
          name: 'get_canvas',
          description: 'Get canvas file contents',
          inputSchema: {
            type: 'object',
            properties: {
              vault: { type: 'string', description: 'Vault name' },
              path: { type: 'string', description: 'Path to canvas file' },
            },
            required: ['vault', 'path'],
          },
        },
        {
          name: 'create_canvas',
          description: 'Create a new canvas file',
          inputSchema: {
            type: 'object',
            properties: {
              vault: { type: 'string', description: 'Vault name' },
              path: { type: 'string', description: 'Path for new canvas' },
            },
            required: ['vault', 'path'],
          },
        },
        {
          name: 'add_canvas_node',
          description: 'Add a node to canvas (file, text, link, or group)',
          inputSchema: {
            type: 'object',
            properties: {
              vault: { type: 'string', description: 'Vault name' },
              canvasPath: { type: 'string', description: 'Path to canvas file' },
              nodeType: { type: 'string', enum: ['file', 'text', 'link', 'group'], description: 'Type of node' },
              x: { type: 'number', description: 'X coordinate' },
              y: { type: 'number', description: 'Y coordinate' },
              width: { type: 'number', description: 'Node width' },
              height: { type: 'number', description: 'Node height' },
              file: { type: 'string', description: 'File path (for file nodes)' },
              text: { type: 'string', description: 'Text content (for text nodes)' },
              url: { type: 'string', description: 'URL (for link nodes)' },
              label: { type: 'string', description: 'Label (for group nodes)' },
              color: { type: 'string', description: 'Node color (1-6)' },
            },
            required: ['vault', 'canvasPath', 'nodeType', 'x', 'y', 'width', 'height'],
          },
        },
        {
          name: 'add_canvas_edge',
          description: 'Add an edge between nodes in canvas',
          inputSchema: {
            type: 'object',
            properties: {
              vault: { type: 'string', description: 'Vault name' },
              canvasPath: { type: 'string', description: 'Path to canvas file' },
              fromNode: { type: 'string', description: 'Source node ID' },
              toNode: { type: 'string', description: 'Target node ID' },
              label: { type: 'string', description: 'Edge label' },
              color: { type: 'string', description: 'Edge color (1-6)' },
            },
            required: ['vault', 'canvasPath', 'fromNode', 'toNode'],
          },
        },
        {
          name: 'list_canvas_files',
          description: 'List all canvas files in vault',
          inputSchema: {
            type: 'object',
            properties: {
              vault: { type: 'string', description: 'Vault name' },
            },
            required: ['vault'],
          },
        },
        // Dataview tools
        {
          name: 'dataview_query',
          description: 'Execute a Dataview-style query on notes',
          inputSchema: {
            type: 'object',
            properties: {
              vault: { type: 'string', description: 'Vault name' },
              from: { type: 'string', description: 'Source filter (folder path or #tag)' },
              where: {
                type: 'array',
                items: {
                  type: 'object',
                  properties: {
                    field: { type: 'string' },
                    operator: { type: 'string', enum: ['eq', 'neq', 'gt', 'gte', 'lt', 'lte', 'contains', 'startsWith', 'endsWith', 'exists'] },
                    value: { type: 'string' }
                  }
                },
                description: 'Filter conditions'
              },
              sort: {
                type: 'array',
                items: {
                  type: 'object',
                  properties: {
                    field: { type: 'string' },
                    direction: { type: 'string', enum: ['asc', 'desc'] }
                  }
                },
                description: 'Sort order'
              },
              groupBy: { type: 'string', description: 'Field to group by' },
              select: { type: 'array', items: { type: 'string' }, description: 'Fields to select' },
              limit: { type: 'number', description: 'Maximum results' }
            },
            required: ['vault'],
          },
        },
        {
          name: 'get_note_metadata',
          description: 'Get metadata for a specific note',
          inputSchema: {
            type: 'object',
            properties: {
              vault: { type: 'string', description: 'Vault name' },
              path: { type: 'string', description: 'Note path' },
            },
            required: ['vault', 'path'],
          },
        },
        {
          name: 'get_unique_values',
          description: 'Get all unique values for a metadata field',
          inputSchema: {
            type: 'object',
            properties: {
              vault: { type: 'string', description: 'Vault name' },
              field: { type: 'string', description: 'Metadata field name' },
            },
            required: ['vault', 'field'],
          },
        },
        // Template tools
        {
          name: 'list_templates',
          description: 'List all available templates',
          inputSchema: {
            type: 'object',
            properties: {
              vault: { type: 'string', description: 'Vault name' },
            },
            required: ['vault'],
          },
        },
        {
          name: 'render_template',
          description: 'Render a template with variables',
          inputSchema: {
            type: 'object',
            properties: {
              vault: { type: 'string', description: 'Vault name' },
              templatePath: { type: 'string', description: 'Path to template file' },
              variables: { type: 'object', description: 'Template variables' },
              frontmatter: { type: 'object', description: 'Additional frontmatter' },
              targetPath: { type: 'string', description: 'Target note path (for context)' },
            },
            required: ['vault', 'templatePath'],
          },
        },
        {
          name: 'create_from_template',
          description: 'Create a new note from a template',
          inputSchema: {
            type: 'object',
            properties: {
              vault: { type: 'string', description: 'Vault name' },
              templatePath: { type: 'string', description: 'Path to template file' },
              targetPath: { type: 'string', description: 'Path for new note' },
              variables: { type: 'object', description: 'Template variables' },
              frontmatter: { type: 'object', description: 'Additional frontmatter' },
            },
            required: ['vault', 'templatePath', 'targetPath'],
          },
        },
        // Periodic notes tools
        {
          name: 'create_daily_note',
          description: 'Create a daily note for a specific date',
          inputSchema: {
            type: 'object',
            properties: {
              vault: { type: 'string', description: 'Vault name' },
              date: { type: 'string', description: 'Date (YYYY-MM-DD), defaults to today' },
              variables: { type: 'object', description: 'Additional template variables' },
            },
            required: ['vault'],
          },
        },
        {
          name: 'create_weekly_note',
          description: 'Create a weekly note for a specific date',
          inputSchema: {
            type: 'object',
            properties: {
              vault: { type: 'string', description: 'Vault name' },
              date: { type: 'string', description: 'Date in the week (YYYY-MM-DD), defaults to this week' },
              variables: { type: 'object', description: 'Additional template variables' },
            },
            required: ['vault'],
          },
        },
        {
          name: 'create_monthly_note',
          description: 'Create a monthly note for a specific date',
          inputSchema: {
            type: 'object',
            properties: {
              vault: { type: 'string', description: 'Vault name' },
              date: { type: 'string', description: 'Date in the month (YYYY-MM-DD), defaults to this month' },
              variables: { type: 'object', description: 'Additional template variables' },
            },
            required: ['vault'],
          },
        },
        {
          name: 'create_yearly_note',
          description: 'Create a yearly note for a specific year',
          inputSchema: {
            type: 'object',
            properties: {
              vault: { type: 'string', description: 'Vault name' },
              date: { type: 'string', description: 'Date in the year (YYYY-MM-DD), defaults to this year' },
              variables: { type: 'object', description: 'Additional template variables' },
            },
            required: ['vault'],
          },
        },
        {
          name: 'get_periodic_note_info',
          description: 'Get info about a periodic note',
          inputSchema: {
            type: 'object',
            properties: {
              vault: { type: 'string', description: 'Vault name' },
              type: { type: 'string', enum: ['daily', 'weekly', 'monthly', 'yearly'], description: 'Note type' },
              date: { type: 'string', description: 'Date (YYYY-MM-DD), defaults to today' },
            },
            required: ['vault', 'type'],
          },
        },
        {
          name: 'list_periodic_notes',
          description: 'List periodic notes of a specific type',
          inputSchema: {
            type: 'object',
            properties: {
              vault: { type: 'string', description: 'Vault name' },
              type: { type: 'string', enum: ['daily', 'weekly', 'monthly', 'yearly'], description: 'Note type' },
              startDate: { type: 'string', description: 'Start date (YYYY-MM-DD)' },
              endDate: { type: 'string', description: 'End date (YYYY-MM-DD)' },
            },
            required: ['vault', 'type'],
          },
        },
      ],
    }));
Behavior2/5

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

No annotations are provided, so the description carries the full burden of behavioral disclosure. It mentions 'suggest' but doesn't clarify whether this is a read-only operation, what permissions are needed, how suggestions are generated, or what the output format might be. For a tool with no annotations, this leaves significant gaps in understanding its behavior.

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

Conciseness5/5

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

The description is a single, efficient sentence with zero wasted words. It's appropriately sized for a simple tool and front-loaded with the core purpose, though it lacks depth due to its brevity.

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

Completeness2/5

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

Given the complexity of suggesting related notes (which could involve algorithms or heuristics), no annotations, and no output schema, the description is incomplete. It doesn't explain how suggestions are generated, what the output looks like, or any behavioral nuances, making it inadequate for informed tool selection.

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

Parameters3/5

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

Schema description coverage is 100%, so the schema fully documents the three parameters (limit, path, vault). The description adds no additional meaning about parameters beyond what's in the schema, such as explaining how 'path' and 'vault' interact or what 'suggestions' entail. Baseline 3 is appropriate when the schema does the heavy lifting.

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

Purpose3/5

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

The description 'Suggest related notes for linking' states the basic purpose (suggest notes for linking) but is vague about scope and mechanism. It doesn't specify whether suggestions are based on content similarity, graph connections, tags, or other criteria, nor does it distinguish this tool from sibling tools like 'get_related_notes' or 'search_notes'.

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?

No guidance is provided on when to use this tool versus alternatives. With sibling tools like 'get_related_notes' and 'search_notes' available, the description offers no context on use cases, prerequisites, or exclusions, leaving the agent to guess based on tool names alone.

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

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/bazylhorsey/obsidian-mcp-server'

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