Skip to main content
Glama

ticket_search

Search project tickets by keywords in title or description, with filters for project, status, priority, and tags to find relevant issues.

Instructions

PROJECT MANAGEMENT (TPM): Search tickets by keyword.

USE THIS TOOL WHEN:

  • User asks "find tickets about X" or "search for Y"

  • Looking for tickets by keywords in title or description

  • Need to discover relevant tickets across projects

Searches title and description. Supports prefix matching (e.g., "org" matches "organization"). Case-insensitive. All filters are combinable.

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
queryYesSearch query (keywords to search in title/description)
project_idNoFilter by project ID (optional, case-insensitive)
statusNoFilter by status (optional)
priorityNoFilter by priority (optional)
tagsNoFilter by tags - ticket must have all specified tags (optional)
limitNoMaximum results to return (default: 20, max: 100)

Implementation Reference

  • Registration of the 'ticket_search' tool in the list_tools() function, including name, description, and JSON inputSchema.
            Tool(
                name="ticket_search",
                description="""PROJECT MANAGEMENT (TPM): Search tickets by keyword.
    
    USE THIS TOOL WHEN:
    - User asks "find tickets about X" or "search for Y"
    - Looking for tickets by keywords in title or description
    - Need to discover relevant tickets across projects
    
    Searches title and description. Supports prefix matching (e.g., "org" matches "organization").
    Case-insensitive. All filters are combinable.""",
                inputSchema={
                    "type": "object",
                    "properties": {
                        "query": {
                            "type": "string",
                            "description": "Search query (keywords to search in title/description)",
                        },
                        "project_id": {
                            "type": "string",
                            "description": "Filter by project ID (optional, case-insensitive)",
                        },
                        "status": {
                            "type": "string",
                            "enum": ["backlog", "planned", "in-progress", "done", "blocked"],
                            "description": "Filter by status (optional)",
                        },
                        "priority": {
                            "type": "string",
                            "enum": ["critical", "high", "medium", "low"],
                            "description": "Filter by priority (optional)",
                        },
                        "tags": {
                            "type": "array",
                            "items": {"type": "string"},
                            "description": "Filter by tags - ticket must have all specified tags (optional)",
                        },
                        "limit": {
                            "type": "integer",
                            "description": "Maximum results to return (default: 20, max: 100)",
                            "default": 20,
                        },
                    },
                    "required": ["query"],
                },
            ),
  • Handler logic in _handle_tool() that processes arguments, normalizes them, calls db.search_tickets(), and formats the JSON response.
    if name == "ticket_search":
        limit = min(args.get("limit", 20), 100)
        status = TicketStatus(args["status"]) if args.get("status") else None
        priority = Priority(args["priority"]) if args.get("priority") else None
    
        results = db.search_tickets(
            query=args["query"],
            project_id=args.get("project_id"),
            status=status,
            priority=priority,
            tags=args.get("tags"),
            limit=limit,
        )
    
        return _json({
            "results": results,
            "total": len(results),
            "query": args["query"],
        })
  • Core implementation of ticket search using SQLite FTS5 full-text search on titles/descriptions, with dynamic filters for project, status, priority, tags, and limit. Returns highlighted snippets.
    def search_tickets(
        self,
        query: str,
        project_id: str | None = None,
        status: TicketStatus | None = None,
        priority: Priority | None = None,
        tags: list[str] | None = None,
        limit: int = 20,
    ) -> list[dict]:
        """Search tickets using full-text search with optional filters.
    
        Args:
            query: Search query (supports prefix matching)
            project_id: Filter by project ID (case-insensitive)
            status: Filter by ticket status
            priority: Filter by priority level
            tags: Filter by tags (ticket must have all specified tags)
            limit: Maximum results to return (default 20)
    
        Returns:
            List of dicts with: id, title, project_id, status, priority, tags, snippet
        """
        if not query or not query.strip():
            return []
    
        # Build FTS5 query with prefix matching for each term
        terms = query.strip().split()
        fts_query = " ".join(f"{term}*" for term in terms)
    
        # Build the SQL query with joins and filters
        sql = """
            SELECT
                t.id,
                t.title,
                t.project_id,
                t.status,
                t.priority,
                t.tags,
                snippet(tickets_fts, 1, '<b>', '</b>', '...', 32) as snippet
            FROM tickets_fts
            JOIN tickets t ON tickets_fts.ticket_id = t.id
            WHERE tickets_fts MATCH ?
        """
        params: list = [fts_query]
    
        # Add filters
        if project_id:
            project_id = self._normalize_id(project_id)
            sql += " AND LOWER(t.project_id) = ?"
            params.append(project_id)
    
        if status:
            sql += " AND t.status = ?"
            params.append(status.value)
    
        if priority:
            sql += " AND t.priority = ?"
            params.append(priority.value)
    
        if tags:
            # Check that ticket has all specified tags using json_each
            for tag in tags:
                sql += " AND EXISTS (SELECT 1 FROM json_each(t.tags) WHERE value = ?)"
                params.append(tag)
    
        # Order by relevance (FTS5 rank) and limit
        sql += " ORDER BY rank LIMIT ?"
        params.append(limit)
    
        try:
            rows = self.conn.execute(sql, params).fetchall()
            return [
                {
                    "id": r["id"],
                    "title": r["title"],
                    "project_id": r["project_id"],
                    "status": r["status"],
                    "priority": r["priority"],
                    "tags": _from_json(r["tags"]),
                    "snippet": r["snippet"],
                }
                for r in rows
            ]
        except Exception:
            # Handle FTS5 syntax errors gracefully
            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/urjitbhatia/tpm-mcp'

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