workflow
Provides context-based workflow guidance and actionable suggestions to optimize tasks in the Obsidian Semantic MCP Server environment.
Instructions
Workflow guidance and suggestions based on current context
Input Schema
| Name | Required | Description | Default |
|---|---|---|---|
| action | Yes | The specific action to perform | |
| type | No | Type of analysis or workflow |
Input Schema (JSON Schema)
{
"properties": {
"action": {
"description": "The specific action to perform",
"enum": [
"suggest"
],
"type": "string"
},
"type": {
"description": "Type of analysis or workflow",
"type": "string"
}
},
"required": [
"action"
],
"type": "object"
}
Implementation Reference
- src/tools/semantic-tools.ts:25-74 (handler)Primary MCP tool handler for the 'workflow' tool (shared with other semantic tools). Instantiates SemanticRouter and calls route() with operation='workflow', then formats response for MCP protocol including workflow hints.handler: async (api: ObsidianAPI, args: any) => { const router = new SemanticRouter(api); const request: SemanticRequest = { operation, action: args.action, params: args }; const response = await router.route(request); // Format for MCP if (response.error) { return { content: [{ type: 'text', text: JSON.stringify({ error: response.error, workflow: response.workflow, context: response.context }, null, 2) }], isError: true }; } // Check if the result is an image file for vault read operations if (operation === 'vault' && args.action === 'read' && response.result && isImageFile(response.result)) { // Return image content for MCP return { content: [{ type: 'image', data: response.result.base64Data, mimeType: response.result.mimeType }] }; } return { content: [{ type: 'text', text: JSON.stringify({ result: response.result, workflow: response.workflow, context: response.context, efficiency_hints: response.efficiency_hints }, null, 2) }] }; }
- src/tools/semantic-tools.ts:77-225 (schema)Schema definitions for 'workflow' tool: description, actions=['suggest'], parameters={type?: string}, integrated into inputSchema with required 'action'.function getOperationDescription(operation: string): string { const descriptions: Record<string, string> = { vault: 'File and folder operations - list, read, create, update, delete, search', edit: 'Smart editing operations - window (auto-buffers content), append, patch, at_line, from_buffer', view: 'Content viewing and navigation - file, window, active, open_in_obsidian', workflow: 'Workflow guidance and suggestions based on current context', system: 'System operations - info, commands, fetch_web' }; return descriptions[operation] || 'Unknown operation'; } function getActionsForOperation(operation: string): string[] { const actions: Record<string, string[]> = { vault: ['list', 'read', 'create', 'update', 'delete', 'search', 'fragments'], edit: ['window', 'append', 'patch', 'at_line', 'from_buffer'], view: ['file', 'window', 'active', 'open_in_obsidian'], workflow: ['suggest'], system: ['info', 'commands', 'fetch_web'] }; return actions[operation] || []; } function getParametersForOperation(operation: string): Record<string, any> { // Common parameters across operations const pathParam = { path: { type: 'string', description: 'Path to the file or directory' } }; const contentParam = { content: { type: 'string', description: 'Content to write or append' } }; // Operation-specific parameters const operationParams: Record<string, Record<string, any>> = { vault: { ...pathParam, directory: { type: 'string', description: 'Directory path for list operations' }, query: { type: 'string', description: 'Search query' }, page: { type: 'number', description: 'Page number for paginated results' }, pageSize: { type: 'number', description: 'Number of results per page' }, strategy: { type: 'string', enum: ['auto', 'adaptive', 'proximity', 'semantic'], description: 'Fragment retrieval strategy (default: auto)' }, maxFragments: { type: 'number', description: 'Maximum number of fragments to return (default: 5)' }, returnFullFile: { type: 'boolean', description: 'Return full file instead of fragments (WARNING: large files can consume significant context)' }, includeContent: { type: 'boolean', description: 'Include file content in search results (slower but more thorough)' }, ...contentParam }, edit: { ...pathParam, ...contentParam, oldText: { type: 'string', description: 'Text to search for (supports fuzzy matching)' }, newText: { type: 'string', description: 'Text to replace with' }, fuzzyThreshold: { type: 'number', description: 'Similarity threshold for fuzzy matching (0-1)', default: 0.7 }, lineNumber: { type: 'number', description: 'Line number for at_line action' }, mode: { type: 'string', enum: ['before', 'after', 'replace'], description: 'Insert mode for at_line action' }, operation: { type: 'string', enum: ['append', 'prepend', 'replace'], description: 'Patch operation: append (add after), prepend (add before), or replace' }, targetType: { type: 'string', enum: ['heading', 'block', 'frontmatter'], description: 'What to target: heading (by path like "H1::H2"), block (by ID), or frontmatter (field)' }, target: { type: 'string', description: 'Target identifier - e.g., "Daily Notes::Today" for heading, block ID, or frontmatter field name' } }, view: { ...pathParam, searchText: { type: 'string', description: 'Text to search for and highlight' }, lineNumber: { type: 'number', description: 'Line number to center view around' }, windowSize: { type: 'number', description: 'Number of lines to show', default: 20 } }, workflow: { type: { type: 'string', description: 'Type of analysis or workflow' } }, system: { url: { type: 'string', description: 'URL to fetch and convert to markdown' } } }; return operationParams[operation] || {}; }
- src/index.ts:80-100 (registration)MCP server registration: sets handlers for ListToolsRequestSchema and CallToolRequestSchema using semanticTools array (includes 'workflow' tool).server.setRequestHandler(ListToolsRequestSchema, async () => { return { tools: semanticTools.map(tool => ({ name: tool.name, description: tool.description, inputSchema: tool.inputSchema })) }; }); // Handle tool execution server.setRequestHandler(CallToolRequestSchema, async (request) => { const { name, arguments: args } = request.params; const tool = semanticTools.find(t => t.name === name); if (!tool) { throw new Error(`Tool not found: ${name}`); } return await tool.handler(obsidianAPI, args); });
- src/semantic/router.ts:654-661 (handler)SemanticRouter.executeWorkflowOperation: routes 'suggest' action (only action) to generateWorkflowSuggestions().private async executeWorkflowOperation(action: string, params: any): Promise<any> { switch (action) { case 'suggest': return this.generateWorkflowSuggestions(); default: throw new Error(`Unknown workflow action: ${action}`); } }
- src/semantic/router.ts:907-941 (helper)Core implementation: generateWorkflowSuggestions provides context-aware suggestions (e.g., continue with last file, refine search) based on SemanticRouter state and context.private generateWorkflowSuggestions(): any { // Generate contextual workflow suggestions based on current state const suggestions: SuggestedAction[] = []; if (this.context.last_file) { suggestions.push({ description: 'Continue working with last file', command: `vault(action='read', path='${this.context.last_file}')`, reason: 'Return to previous work' }); } if (this.context.search_history?.length) { const lastSearch = this.context.search_history[this.context.search_history.length - 1]; suggestions.push({ description: 'Refine last search', command: `vault(action='search', query='${lastSearch} AND ...')`, reason: 'Narrow down results' }); } // Always include a default suggestion if no context-specific ones if (suggestions.length === 0) { suggestions.push({ description: 'Use workflow hints from other operations', command: 'vault(action="list") or vault(action="read", path="...") etc.', reason: 'Each operation provides contextual workflow suggestions' }); } return { current_context: this.getCurrentContext(), suggestions }; }