Skip to main content
Glama
TAgents

Planning System MCP Server

by TAgents

search

Search plans, nodes, and content within a planning system to find specific information using queries and filters.

Instructions

Universal search tool for plans, nodes, and content

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
scopeNoSearch scopeglobal
scope_idNoPlan ID (if scope is 'plan') or Node ID (if scope is 'node')
queryYesSearch query
filtersNoOptional filters

Implementation Reference

  • Handler for the 'search' MCP tool. Dispatches based on scope to globalSearch or searchPlan wrappers from search-wrapper.js, or client-side filtering for plans. Applies additional filters and formats response using formatResponse.
    if (name === "search") {
      const { scope, scope_id, query, filters = {} } = args;
      
      let results = [];
      
      switch (scope) {
        case "global":
          // Global search across all plans
          const searchWrapper = require('./tools/search-wrapper');
          results = await searchWrapper.globalSearch(query);
          break;
          
        case "plans":
          // Search only in plan titles/descriptions
          const plans = await apiClient.plans.getPlans();
          
          // Handle wildcard queries
          if (query === '*' || query === '' || !query) {
            // Return all plans (with optional status filter)
            results = plans.filter(plan => 
              !filters.status || plan.status === filters.status
            );
          } else {
            // Normal search
            const queryLower = query.toLowerCase();
            results = plans.filter(plan => {
              const titleMatch = plan.title.toLowerCase().includes(queryLower);
              const descMatch = plan.description?.toLowerCase().includes(queryLower);
              const statusMatch = !filters.status || plan.status === filters.status;
              return (titleMatch || descMatch) && statusMatch;
            });
          }
          break;
          
        case "plan":
          // Search within a specific plan
          if (!scope_id) {
            throw new Error("scope_id (plan_id) is required when scope is 'plan'");
          }
          const searchWrapperPlan = require('./tools/search-wrapper');
          results = await searchWrapperPlan.searchPlan(scope_id, query);
          break;
          
        case "node":
          // Search within a specific node's children
          if (!scope_id) {
            throw new Error("scope_id (node_id) is required when scope is 'node'");
          }
          // This would need a specific implementation
          results = [];
          break;
          
        default:
          // Default to global search
          const searchWrapperDefault = require('./tools/search-wrapper');
          results = await searchWrapperDefault.globalSearch(query);
      }
      
      // Apply filters
      if (filters.type) {
        results = results.filter(item => item.type === filters.type);
      }
      if (filters.limit) {
        results = results.slice(0, filters.limit);
      }
      
      return formatResponse({
        query,
        scope,
        scope_id,
        filters,
        count: results.length,
        results
      });
    }
  • Schema definition for the 'search' tool, defining input parameters: scope (global/plans/plan/node), scope_id, query (required), and optional filters (status, type, limit).
      name: "search",
      description: "Universal search tool for plans, nodes, and content",
      inputSchema: {
        type: "object",
        properties: {
          scope: { 
            type: "string",
            description: "Search scope",
            enum: ["global", "plans", "plan", "node"],
            default: "global"
          },
          scope_id: { 
            type: "string", 
            description: "Plan ID (if scope is 'plan') or Node ID (if scope is 'node')"
          },
          query: { 
            type: "string", 
            description: "Search query"
          },
          filters: {
            type: "object",
            description: "Optional filters",
            properties: {
              status: { 
                type: "string",
                description: "Filter by status",
                enum: ["draft", "active", "completed", "archived", "not_started", "in_progress", "blocked"]
              },
              type: {
                type: "string",
                description: "Filter by type",
                enum: ["plan", "node", "phase", "task", "milestone", "artifact", "log"]
              },
              limit: {
                type: "integer",
                description: "Maximum number of results",
                default: 20
              }
            }
          }
        },
        required: ["query"]
      }
    },
  • src/index.js:4-54 (registration)
    Registers the MCP tools on the server instance by calling setupTools(server), which includes the 'search' tool.
    const { setupTools } = require('./tools');
    require('dotenv').config();
    
    /**
     * Initialize the Planning System MCP Server
     * 
     * Features:
     * - Simplified architecture with tools-only interface
     * - Full CRUD operations on all entities
     * - Unified search across all scopes
     * - Batch operations for efficiency
     * - Structured JSON responses
     * - Comprehensive logging system
     */
    async function main() {
      const isDev = process.env.NODE_ENV === 'development';
      
      if (isDev) {
        console.error('Initializing Planning System MCP Server...');
      }
      
      try {
        // Log environment settings
        console.error(`API URL: ${process.env.API_URL || 'http://localhost:3000'}`);
        
        // Check for token
        const userApiToken = process.env.USER_API_TOKEN || process.env.API_TOKEN;
        console.error(`User API Token: ${userApiToken ? '***' + userApiToken.slice(-4) : 'NOT SET'}`);
        console.error(`MCP Server Name: ${process.env.MCP_SERVER_NAME || 'planning-system-mcp'}`);
        console.error(`MCP Server Version: ${process.env.MCP_SERVER_VERSION || '0.2.0'}`);
        
        // Validate required environment variables
        if (!userApiToken) {
          throw new Error('USER_API_TOKEN environment variable is required. Please generate one from the Agent Planner UI and set it in .env file.');
        }
        
        // Create MCP server instance
        const server = new Server({
          name: process.env.MCP_SERVER_NAME || "planning-system-mcp",
          version: process.env.MCP_SERVER_VERSION || "0.2.0"
        }, {
          capabilities: {
            tools: {}
          }
        });
    
        console.error('MCP Server created');
        
        // Setup tools
        setupTools(server);
  • Helper function searchPlan that wraps apiClient.search.searchPlan, normalizes response to return results array directly.
    async function searchPlan(planId, query) {
      try {
        // Call the original search function from the API client
        const response = await apiClient.search.searchPlan(planId, query);
        
        // Log the actual response for debugging
        if (process.env.NODE_ENV === 'development') {
          console.log('Search plan response:', typeof response, Object.keys(response || {}));
        }
        
        // Handle different response formats
        if (!response) {
          return [];
        }
        
        // If response is already an array, return it
        if (Array.isArray(response)) {
          return response;
        }
        
        // If response has results array
        if (response.results && Array.isArray(response.results)) {
          return response.results;
        }
        
        // If response has other properties that are arrays
        const results = [];
        Object.keys(response).forEach(key => {
          if (Array.isArray(response[key])) {
            response[key].forEach(item => {
              results.push({
                ...item,
                type: item.type || key,
                source: 'plan_search'
              });
            });
          }
        });
        
        if (results.length > 0) {
          return results;
        }
        
        // Try to parse response as a search result object
        if (response.query !== undefined && response.count !== undefined) {
          return response.results || [];
        }
        
        console.error('Unexpected search response format:', JSON.stringify(response).substring(0, 200));
        return [];
      } catch (error) {
        console.error('Error in searchPlan wrapper:', error.message);
        return [];
      }
    }
  • Helper function globalSearch that wraps apiClient.search.globalSearch, handles various response formats, flattens and enriches results with type, category, source.
    async function globalSearch(query) {
      try {
        // Call the original global search function
        const response = await apiClient.search.globalSearch(query);
        
        if (process.env.NODE_ENV === 'development') {
          console.log('Global search response type:', typeof response);
          if (response) {
            console.log('Response keys:', Object.keys(response));
          }
        }
        
        // Handle different response formats
        if (!response) {
          return [];
        }
        
        // If response is already an array of results
        if (Array.isArray(response)) {
          return response.map(item => ({
            ...item,
            type: item.type || 'unknown',
            source: 'global_search'
          }));
        }
        
        // If response has a results property
        if (response.results !== undefined) {
          // If results is already an array, return it
          if (Array.isArray(response.results)) {
            return response.results.map(item => ({
              ...item,
              type: item.type || 'unknown',
              source: 'global_search'
            }));
          }
          
          // If results is an object with categories
          if (typeof response.results === 'object') {
            const allResults = [];
            
            // Collect results from each category (plans, nodes, comments, etc.)
            Object.keys(response.results).forEach(category => {
              if (Array.isArray(response.results[category])) {
                // Add category type to each result
                const categoryResults = response.results[category].map(item => ({
                  ...item,
                  type: item.type || category,
                  category,
                  source: 'global_search'
                }));
                allResults.push(...categoryResults);
              }
            });
            
            return allResults;
          }
        }
        
        // Check if response has categorized results (plans, nodes, etc.)
        const categories = ['plans', 'nodes', 'comments', 'logs', 'artifacts'];
        const allResults = [];
        
        categories.forEach(category => {
          if (response[category] && Array.isArray(response[category])) {
            response[category].forEach(item => {
              allResults.push({
                ...item,
                type: item.type || category.slice(0, -1), // Remove 's' from category
                category,
                source: 'global_search'
              });
            });
          }
        });
        
        if (allResults.length > 0) {
          return allResults;
        }
        
        // Generic handler for any object with arrays
        Object.keys(response).forEach(key => {
          if (Array.isArray(response[key]) && key !== 'results') {
            response[key].forEach(item => {
              allResults.push({
                ...item,
                type: item.type || key,
                category: key,
                source: 'global_search'
              });
            });
          }
        });
        
        return allResults;
      } catch (error) {
        console.error('Error in globalSearch wrapper:', error.message);
        if (error.response) {
          console.error('API error status:', error.response.status);
          console.error('API error data:', error.response.data);
        }
        return [];
      }
    }

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/TAgents/agent-planner-mcp'

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