Skip to main content
Glama

search

Find relevant Slack conversations using semantic search with Cohere reranking. Filter results by channel, user, or content type to locate specific discussions and linked resources.

Instructions

Search the Slack index using semantic similarity with Cohere reranking.

Args: query: Natural language search query. n_results: Number of results to return (default 10, max 50). source_filter: Filter by source type. One of: slack_thread, github_issue, github_pr, github_file, linear_issue, notion_page. channel_filter: Filter by Slack channel name (partial match). user_filter: Filter by participant name (partial match on thread_users). include_links: If False, exclude linked resources and return only Slack threads.

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
queryYes
n_resultsNo
source_filterNo
channel_filterNo
user_filterNo
include_linksNo

Implementation Reference

  • The main handler function for the 'search' MCP tool. Implements semantic search over indexed Slack data using vector similarity with Cohere reranking. Accepts parameters for query, result count, and filters (source, channel, user). Returns ranked results with metadata.
    @mcp.tool()
    def search(
        query: str,
        n_results: int = 10,
        source_filter: str | None = None,
        channel_filter: str | None = None,
        user_filter: str | None = None,
        include_links: bool = True,
    ) -> dict:
        """Search the Slack index using semantic similarity with Cohere reranking.
    
        Args:
            query: Natural language search query.
            n_results: Number of results to return (default 10, max 50).
            source_filter: Filter by source type. One of: slack_thread, github_issue,
                           github_pr, github_file, linear_issue, notion_page.
            channel_filter: Filter by Slack channel name (partial match).
            user_filter: Filter by participant name (partial match on thread_users).
            include_links: If False, exclude linked resources and return only Slack threads.
        """
        store = _get_store()
        reranker = _get_reranker()
        n_results = min(max(1, n_results), 50)
    
        where_clauses: List[Dict] = []
        if source_filter:
            if source_filter not in SOURCE_TYPES:
                return {
                    "error": f"Invalid source_filter. Must be one of: {sorted(SOURCE_TYPES)}"
                }
            where_clauses.append({"source": source_filter})
        elif not include_links:
            where_clauses.append({"source": "slack_thread"})
    
        if channel_filter:
            where_clauses.append({"channel_name": {"$contains": channel_filter}})
        if user_filter:
            where_clauses.append({"thread_users": {"$contains": user_filter}})
    
        where = None
        if len(where_clauses) == 1:
            where = where_clauses[0]
        elif len(where_clauses) > 1:
            where = {"$and": where_clauses}
    
        fetch_n = n_results * 3
        kwargs: Dict[str, Any] = {"query_texts": [query], "n_results": fetch_n}
        if where:
            kwargs["where"] = where
    
        try:
            results = store.query(**kwargs)
            candidates_ids = results["ids"][0]
            candidates_docs = results["documents"][0]
            candidates_distances = results["distances"][0]
            candidates_metas = results["metadatas"][0]
    
            if not candidates_docs:
                return {"query": query, "count": 0, "results": []}
    
            try:
                rerank_results = reranker.rerank(
                    query=query, documents=candidates_docs, top_n=n_results
                )
                formatted = []
                for r in rerank_results:
                    idx = r["index"]
                    formatted.append(
                        {
                            "id": candidates_ids[idx],
                            "text": candidates_docs[idx],
                            "rerank_score": round(r["score"], 4),
                            "vector_distance": round(candidates_distances[idx], 4),
                            "metadata": candidates_metas[idx],
                        }
                    )
            except Exception as e:
                logger.warning(f"Reranker failed, falling back to vector results: {e}")
                formatted = []
                for i in range(min(n_results, len(candidates_docs))):
                    formatted.append(
                        {
                            "id": candidates_ids[i],
                            "text": candidates_docs[i],
                            "rerank_score": None,
                            "vector_distance": round(candidates_distances[i], 4),
                            "metadata": candidates_metas[i],
                        }
                    )
    
            return {"query": query, "count": len(formatted), "results": formatted}
        except Exception as e:
            logger.error(f"Error in search: {e}")
            return {"error": str(e)}
  • Input schema definition for the search tool. Defines typed parameters: query (string), n_results (int, default 10), source_filter (optional string), channel_filter (optional string), user_filter (optional string), and include_links (bool, default True).
        query: str,
        n_results: int = 10,
        source_filter: str | None = None,
        channel_filter: str | None = None,
        user_filter: str | None = None,
        include_links: bool = True,
    ) -> dict:
  • server.py:107-107 (registration)
    Registration of the search tool with the MCP server using the @mcp.tool() decorator, which exposes the function as an MCP tool named 'search'.
    @mcp.tool()
  • QdrantVectorStore.query() method - helper function that performs the actual vector similarity search against the Qdrant database. Generates embeddings for queries, applies filters, and returns matching documents with distances and metadata.
    def query(
        self, query_texts: List[str], n_results: int = 10, **kwargs: Any
    ) -> Dict[str, Any]:
        query_embeddings = self.embedder.embed_queries(query_texts)
    
        all_ids: List[List[str]] = []
        all_docs: List[List[str]] = []
        all_distances: List[List[float]] = []
        all_metadatas: List[List[Dict]] = []
    
        qdrant_filter = None
        if "where" in kwargs:
            qdrant_filter = _translate_filter(kwargs["where"])
    
        for query_embedding in query_embeddings:
            results = self.client.query_points(
                collection_name=self.collection_name,
                query=query_embedding,
                limit=n_results,
                query_filter=qdrant_filter,
                with_payload=True,
            ).points
    
            ids, docs, distances, metadatas = [], [], [], []
            for point in results:
                ids.append(point.payload.get("_string_id", str(point.id)))
                docs.append(point.payload.get("document", ""))
                distances.append(
                    1.0 - point.score if point.score is not None else 1.0
                )
                meta = {
                    k: v
                    for k, v in point.payload.items()
                    if k not in ("document", "_string_id")
                }
                metadatas.append(meta)
    
            all_ids.append(ids)
            all_docs.append(docs)
            all_distances.append(distances)
            all_metadatas.append(metadatas)
    
        return {
            "ids": all_ids,
            "documents": all_docs,
            "distances": all_distances,
            "metadatas": all_metadatas,
        }
  • Reranker.rerank() method - helper function that reranks search results using Cohere's reranking model on AWS Bedrock. Takes query and candidate documents, returns top_n results with relevance scores.
    def rerank(
        self, query: str, documents: List[str], top_n: int = 10
    ) -> List[Dict[str, Any]]:
        response = self.client.rerank(
            model=self.model, query=query, documents=documents, top_n=top_n
        )
        return [
            {"index": r.index, "score": r.relevance_score} for r in response.results
        ]
Behavior4/5

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

With no annotations provided, the description carries full burden and adds valuable behavioral context: it explains the search method (semantic similarity with Cohere reranking), filtering capabilities (source, channel, user), and the include_links parameter's effect on results. However, it doesn't mention rate limits, authentication needs, or error handling, leaving some gaps.

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 well-structured and front-loaded with the core purpose, followed by a clear Args section. Each sentence adds value: the first explains the search method, and subsequent lines efficiently detail parameters without redundancy. It's appropriately sized for a 6-parameter tool.

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

Completeness4/5

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

Given 6 parameters, 0% schema coverage, no annotations, and no output schema, the description does an excellent job explaining inputs and behavior. However, it lacks details on output format (e.g., result structure) and error conditions, which would help the agent use it more effectively. It's nearly complete but has minor gaps.

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

Parameters5/5

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

Schema description coverage is 0%, so the description must compensate. It provides detailed semantics for all 6 parameters: query purpose, n_results defaults and limits, source_filter enum values, channel_filter/user_filter matching behavior, and include_links effect. This adds significant meaning beyond the bare schema.

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

Purpose5/5

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

The description clearly states the tool's purpose: 'Search the Slack index using semantic similarity with Cohere reranking.' It specifies the verb ('Search'), resource ('Slack index'), and method ('semantic similarity with Cohere reranking'), distinguishing it from siblings like list_channels or get_thread that perform different operations.

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

Usage Guidelines3/5

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

The description implies usage through parameter explanations (e.g., source_filter options, include_links behavior) but does not explicitly state when to use this tool versus alternatives like collection_stats or get_thread. It provides context for filtering but lacks direct guidance on tool selection scenarios.

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/KanvaBhatia-Alaan/alaan-slack-indexed-mcp-tool'

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