Skip to main content
Glama
adrielisa

MCP Server: Weather & Upnify Integration

by adrielisa

search-upnify-contacts

Find prospects and clients in Upnify CRM using a name, email, or phone number. Specify search terms and set the number of records to retrieve.

Instructions

Search for contacts (prospects and clients) in Upnify by name, email, or phone

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
buscarYesSearch term: name, email, or phone number
cantidadRegistrosNoNumber of records to return

Implementation Reference

  • The core handler function that authenticates with Upnify, performs a GET request to the search endpoint with parameters 'buscar' and 'cantidadRegistros', parses the JSON response, and returns formatted results or error.
    async def search_contacts(self, search_term: str, limit: int = 10) -> Dict[str, Any]:
        """Search for contacts (prospects and clients)"""
        auth_info = await self.auth.get_token_and_user_info()
        token = auth_info["token"]
        
        if not search_term:
            raise ValueError("El término de búsqueda es obligatorio")
        
        if limit > DEFAULTS["MAX_SEARCH_RESULTS"]:
            limit = DEFAULTS["MAX_SEARCH_RESULTS"]
        
        async with httpx.AsyncClient() as client:
            try:
                response = await client.get(
                    f"{API_URLS['UPNIFY_BASE']}{ENDPOINTS['SEARCH_CONTACTS']}",
                    headers={
                        "token": token,
                        "Content-Type": "application/json"
                    },
                    params={
                        "buscar": search_term,
                        "cantidadRegistros": limit
                    }
                )
                
                if not response.is_success:
                    error_text = response.text
                    raise Exception(f"Error en búsqueda: {response.status_code} {response.reason_phrase}. {error_text}")
                
                contacts = response.json()
                
                return {
                    "success": True,
                    "message": f"Búsqueda completada. {len(contacts)} resultados encontrados",
                    "contacts": contacts,
                    "searchTerm": search_term,
                    "totalResults": len(contacts)
                }
                
            except Exception as error:
                return {
                    "success": False,
                    "error": f"Error al buscar contactos en Upnify: {str(error)}"
                }
  • The input schema defining parameters for the search-upnify-contacts tool: 'buscar' (required string) and 'cantidadRegistros' (optional integer, default 10).
    inputSchema={
        "type": "object",
        "properties": {
            "buscar": {"type": "string", "description": "Search term: name, email, or phone number"},
            "cantidadRegistros": {"type": "integer", "description": "Number of records to return", "default": 10, "minimum": 1, "maximum": 100}
        },
        "required": ["buscar"]
    }
  • Registration of the tool in the MCP server's list_tools handler, including name, description, and schema.
    Tool(
        name="search-upnify-contacts",
        description="Search for contacts (prospects and clients) in Upnify by name, email, or phone",
        inputSchema={
            "type": "object",
            "properties": {
                "buscar": {"type": "string", "description": "Search term: name, email, or phone number"},
                "cantidadRegistros": {"type": "integer", "description": "Number of records to return", "default": 10, "minimum": 1, "maximum": 100}
            },
            "required": ["buscar"]
        }
    ),
  • TypeScript implementation of the searchContacts method in ProspectsHandler class. Authenticates, adds 'secciones' param, fetches from Upnify SEARCH endpoint, processes response to filter prospects/clients, formats results with summary.
    async searchContacts(tkIntegracion: string, searchParams: { buscar: string; cantidadRegistros: number }) {
        try {
            const { token, userInfo } = await this.auth.getTokenAndUserInfo(tkIntegracion);
    
            const queryParams = new URLSearchParams({
                buscar: searchParams.buscar,
                cantidadRegistros: searchParams.cantidadRegistros.toString(),
                secciones: DEFAULTS.SEARCH_SECTIONS
            });
    
            const response = await fetch(`${API_URLS.UPNIFY_BASE}${ENDPOINTS.SEARCH}?${queryParams}`, {
                method: 'GET',
                headers: {
                    'token': token,
                    'Content-Type': 'application/json',
                    'User-Agent': 'UpnifyMCP/1.0'
                }
            });
    
            if (!response.ok) {
                const errorText = await response.text();
                console.error('Parámetros enviados:', searchParams);
                console.error('Token usado:', token);
                console.error('Respuesta del servidor:', errorText);
                throw new Error(`Error al buscar contactos: ${response.status} ${response.statusText}. ${errorText}`);
            }
    
            const resultados = await response.json();
    
            if (!Array.isArray(resultados) || resultados.length < 2) {
                return {
                    success: true,
                    message: 'Búsqueda completada sin resultados',
                    termino: searchParams.buscar,
                    total: 0,
                    resumen: { prospectos: 0, clientes: 0 },
                    contactos: [],
                    tkEmpresa: userInfo.tkEmpresa
                };
            }
    
            // resultados[0] contiene el resumen, resultados[1] contiene los datos
            const resumen = resultados[0][0] || {};
            const contactosRaw = resultados[1] || [];
    
            // Filtrar y formatear solo prospectos y clientes
            const contactos = contactosRaw
                .filter((item: any) => item.seccion === 'prospectos' || item.seccion === 'clientes')
                .map((contacto: any) => ({
                    seccion: contacto.seccion,
                    tkProspecto: contacto.tkProspecto,
                    contacto: contacto.contacto,
                    correo: contacto.correo,
                    telefono: contacto.telefono,
                    movil: contacto.movil,
                    ejecutivo: contacto.ejecutivo,
                    ejecutivoIniciales: contacto.ejecutivoIniciales,
                    empresa: contacto.empresa || '',
                    id: `${contacto.seccion}-${contacto.tkProspecto}`
                }));
    
            return {
                success: true,
                message: 'Búsqueda completada exitosamente',
                termino: searchParams.buscar,
                total: contactos.length,
                resumen: {
                    prospectos: resumen.prospectos || 0,
                    clientes: resumen.clientes || 0
                },
                contactos: contactos,
                mensaje: contactos.length > 1
                    ? `Se encontraron ${contactos.length} contactos. Para crear una oportunidad, especifica el tkProspecto del contacto deseado.`
                    : contactos.length === 1
                        ? 'Se encontró 1 contacto exacto'
                        : 'No se encontraron contactos',
                tkEmpresa: userInfo.tkEmpresa
            };
    
        } catch (error) {
            throw new Error(`Error al buscar contactos en Upnify: ${error instanceof Error ? error.message : error}`);
        }
  • main.ts:204-223 (registration)
    Tool registration in the TypeScript MCP server's listTools response, defining name, description, and input schema.
        name: 'search-upnify-contacts',
        description: 'Search for contacts (prospects and clients) in Upnify by name, email, or phone',
        inputSchema: {
            type: 'object',
            properties: {
                buscar: {
                    type: 'string',
                    description: 'Search term: name, email, or phone number'
                },
                cantidadRegistros: {
                    type: 'integer',
                    description: 'Number of records to return',
                    default: 10,
                    minimum: 1,
                    maximum: 100
                }
            },
            required: ['buscar']
        }
    },
Behavior2/5

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

With no annotations provided, the description carries the full burden of behavioral disclosure. It states the search functionality but lacks details on permissions, rate limits, pagination, or response format. 'Search for contacts' implies read-only, but no explicit safety or operational context is given.

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 waste. It front-loads the core purpose and includes essential search criteria, making it easy to parse quickly.

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

Completeness3/5

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

For a simple search tool with 2 parameters and 100% schema coverage, the description is adequate but incomplete. No output schema exists, and with no annotations, it should ideally mention response structure or limitations. It covers the basics but lacks depth for full agent understanding.

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 already documents both parameters thoroughly. The description adds minimal value by mentioning search fields (name, email, phone) which aligns with 'buscar', but doesn't provide additional syntax or format details beyond the schema.

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

Purpose4/5

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

The description clearly states the action ('Search for contacts') and the resource ('contacts in Upnify'), specifying the searchable fields (name, email, phone). It distinguishes from siblings by focusing on search rather than creation or reporting, though it doesn't explicitly name alternatives for similar search functions.

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?

The description provides no guidance on when to use this tool versus alternatives. It doesn't mention prerequisites, context for searching contacts, or differentiate from potential sibling search tools (none listed, but no explicit comparison).

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

Related 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/adrielisa/MCP'

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