Skip to main content
Glama

query_jobs

Search and filter Web3 job listings from web3.career by remote status, country, or specific skills to find relevant opportunities in the blockchain industry.

Instructions

Query Web3 job listings from the web3.career API with optional filters.

Returns a Markdown-formatted list of jobs, including Job ID, Title, Company, Location,
Remote status, Published At date, Apply URL, and Description (always included).

Parameters:
    remote (Optional[bool]): Filter for remote jobs. Set to True for remote-only,
        False for non-remote, or None for no filter. Defaults to None.
    country (Optional[str]): Filter by country (e.g., "United States", "Canada").
        Case-insensitive, spaces are converted to hyphens (e.g., "united-states").
        Defaults to None (no country filter).
    tag (Optional[str]): Filter by job tag (e.g., "react", "blockchain").
        Case-insensitive. Defaults to None (no tag filter).
    limit (Optional[int]): Maximum number of jobs to return. Must be between 1 and 100.
        Defaults to 50. Raises ValueError if limit exceeds 100.
    ctx (Context): MCP context for logging and internal use. Automatically provided
        by the MCP framework. Defaults to None.

Returns:
    str: A Markdown string containing a list of job listings with the specified fields.

Notes:
    - Descriptions are always included (show_description is hardcoded to True),
      truncated to 100 characters, and have HTML tags removed.
    - Published At is derived from the 'date_epoch' field, formatted as YYYY-MM-DD.
    - Apply URL is included as a clickable Markdown link for each job.
    - Source is credited to web3.career per API terms.

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
countryNo
limitNo
remoteNo
tagNo

Implementation Reference

  • main.py:78-128 (handler)
    The main handler function for the 'query_jobs' tool. It is decorated with @mcp.tool() for registration, defines input parameters with type hints serving as schema, fetches jobs using helper functions, formats them into Markdown, and returns the result.
    @mcp.tool()
    async def query_jobs(
        remote: Optional[bool] = None,
        country: Optional[str] = None,
        tag: Optional[str] = None,
        limit: Optional[int] = 50,
        ctx: Context = None
    ) -> str:
        """
        Query Web3 job listings from the web3.career API with optional filters.
        
        Returns a Markdown-formatted list of jobs, including Job ID, Title, Company, Location,
        Remote status, Published At date, Apply URL, and Description (always included).
        
        Parameters:
            remote (Optional[bool]): Filter for remote jobs. Set to True for remote-only,
                False for non-remote, or None for no filter. Defaults to None.
            country (Optional[str]): Filter by country (e.g., "United States", "Canada").
                Case-insensitive, spaces are converted to hyphens (e.g., "united-states").
                Defaults to None (no country filter).
            tag (Optional[str]): Filter by job tag (e.g., "react", "blockchain").
                Case-insensitive. Defaults to None (no tag filter).
            limit (Optional[int]): Maximum number of jobs to return. Must be between 1 and 100.
                Defaults to 50. Raises ValueError if limit exceeds 100.
            ctx (Context): MCP context for logging and internal use. Automatically provided
                by the MCP framework. Defaults to None.
        
        Returns:
            str: A Markdown string containing a list of job listings with the specified fields.
        
        Notes:
            - Descriptions are always included (show_description is hardcoded to True),
              truncated to 100 characters, and have HTML tags removed.
            - Published At is derived from the 'date_epoch' field, formatted as YYYY-MM-DD.
            - Apply URL is included as a clickable Markdown link for each job.
            - Source is credited to web3.career per API terms.
        """
        if limit > 100:
            raise ValueError("Limit cannot exceed 100")
        params = {"limit": limit}
        if remote is not None:
            params["remote"] = str(remote).lower()
        if country:
            params["country"] = country.lower().replace(" ", "-")
        if tag:
            params["tag"] = tag.lower()
    
        ctx.info(f"Querying jobs with parameters: {params}")
        jobs = await fetch_jobs(params)
        ctx.info(f"Retrieved {len(jobs)} jobs")
        return jobs_to_markdown(jobs, show_description=True)
  • main.py:23-40 (helper)
    Helper function that performs the actual API call to fetch jobs from web3.career using the provided parameters.
    async def fetch_jobs(params: Dict[str, Any] = None) -> List[Dict]:
        """Fetch jobs from web3.career API with given parameters."""
        async with httpx.AsyncClient() as client:
            try:
                params = params or {}
                params["token"] = API_TOKEN
                response = await client.get(BASE_URL, params=params)
                response.raise_for_status()
                # API returns a list with metadata and job listings
                data = response.json()
                if len(data) > 2:
                    return data[2]  # Job listings are in the third element
                return []
            except httpx.HTTPStatusError as e:
                raise ValueError(f"API request failed: {e}")
            except Exception as e:
                raise ValueError(f"Error fetching jobs: {e}")
  • main.py:51-75 (helper)
    Helper function that converts the list of jobs into a formatted Markdown string, including cleaning HTML from descriptions and formatting dates.
    def jobs_to_markdown(jobs: List[Dict], show_description: bool) -> str:
        """Convert job list to a Markdown list."""
        lines = ["# Web3 Job Listings\n"]
        for job in jobs:
            description = strip_html(job.get("description", "")) if show_description else ""
            # Truncate description to 100 characters for readability
            if description:
                description = description[:100] + ("..." if len(description) > 100 else "")
            apply_url = f"[Apply]({job['apply_url']})"
            # Get publication date from 'date_epoch'
            dt = datetime.fromtimestamp(int(job["date_epoch"]))
            published_at = dt.strftime("%Y-%m-%d")
            lines.append(f"- **Job ID**: {job['id']}")
            lines.append(f"  - **Title**: {job['title']}")
            lines.append(f"  - **Company**: {job['company']}")
            lines.append(f"  - **Location**: {job['location']}")
            lines.append(f"  - **Remote**: {'Yes' if job['is_remote'] else 'No'}")
            lines.append(f"  - **Published At**: {published_at}")
            lines.append(f"  - **Apply URL**: {apply_url}")
            if show_description:
                lines.append(f"  - **Description**: {description}")
            lines.append("")  # Blank line between jobs
        
        lines.append("*Source: web3.career*")
        return "\n".join(lines)
  • main.py:41-49 (helper)
    Utility helper to strip HTML tags from job descriptions and normalize whitespace.
    def strip_html(text: str) -> str:
        """Remove HTML tags from text and clean up whitespace."""
        if not text:
            return ""
        # Remove HTML tags
        clean = re.sub(r'<[^>]+>', '', text)
        # Replace multiple whitespace with single space, strip newlines
        clean = re.sub(r'\s+', ' ', clean).strip()
        return clean
  • main.py:78-78 (registration)
    The @mcp.tool() decorator registers the query_jobs function as an MCP tool.
    @mcp.tool()
Behavior4/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 effectively describes key behaviors: the return format (Markdown-formatted list), data processing (descriptions truncated to 100 characters, HTML tags removed, date formatting), and constraints (limit must be 1-100, raises ValueError if exceeded). It also notes source attribution per API terms. However, it doesn't cover potential errors beyond limit validation or rate limits, leaving minor gaps.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is well-structured with clear sections (purpose, parameters, returns, notes) and front-loaded key information. It is appropriately sized for the tool's complexity, but some sentences could be more concise (e.g., the parameter explanations are detailed but slightly verbose). Overall, it earns its place with useful details without excessive fluff.

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 the tool's moderate complexity (4 parameters, no annotations, no output schema), the description is largely complete. It covers purpose, parameters, return format, and behavioral notes. However, it lacks explicit error handling details beyond the 'limit' validation and doesn't mention pagination or API rate limits, which could be relevant for a query tool. The absence of an output schema is mitigated by describing the return value.

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?

The schema description coverage is 0%, so the description must fully compensate. It provides detailed semantics for all 4 parameters: explains what each filter does (e.g., 'remote' for remote jobs, 'country' with case-insensitivity and formatting rules), specifies defaults, and includes validation for 'limit'. The 'ctx' parameter is noted as automatically provided, clarifying its internal use. This adds significant value 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: 'Query Web3 job listings from the web3.career API with optional filters.' It specifies the verb ('query'), resource ('Web3 job listings'), and source ('web3.career API'), making it highly specific. Since there are no sibling tools, no differentiation is needed, but the description stands alone as complete.

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 the mention of 'optional filters' and the detailed parameter explanations, suggesting when to use specific filters. However, it lacks explicit guidance on when to use this tool versus alternatives (e.g., for job searches vs. other data queries), and there are no sibling tools to compare against. This results in implied but not explicit usage guidelines.

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/kukapay/web3-jobs-mcp'

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