Skip to main content
Glama

lookup_far_clause

Read-only

Look up current FAR or DFARS clause text by section identifier. Returns parsed clean text with headings, paragraphs, and citations, auto-resolving latest available date.

Instructions

Convenience tool: look up the current text of a FAR or DFARS clause.

Pass a section identifier like '15.305', '52.212-4', '2.101', etc. Default chapter='1' (FAR). Use chapter='2' for DFARS (e.g., '252.227-7014').

Auto-resolves the latest available date. Returns parsed clean text with heading, paragraphs, and citations.

Common FAR sections: 2.101 (Definitions), 9.104-1 (Responsibility), 15.305 (Proposal Evaluation), 19.502-2 (Small Business Set-Asides), 52.212-4 (Commercial Terms), 52.212-5 (Required Commercial Terms).

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
section_idYes
chapterNo1
dateNo

Output Schema

TableJSON Schema
NameRequiredDescriptionDefault

No arguments

Implementation Reference

  • The tool handler function for 'lookup_far_clause'. Decorated with @mcp.tool, it takes a section_id, chapter (default '1' for FAR), and optional date. Coerces input, validates chapter against Title 48 chapters, resolves date if needed, then delegates to get_cfr_content to fetch and parse the CFR text from the eCFR API.
    @mcp.tool(annotations={"title": "Lookup FAR Clause", "readOnlyHint": True, "destructiveHint": False})
    async def lookup_far_clause(
        section_id: Any,
        chapter: Any = "1",
        date: str | None = None,
    ) -> dict[str, Any]:
        """Convenience tool: look up the current text of a FAR or DFARS clause.
    
        Pass a section identifier like '15.305', '52.212-4', '2.101', etc.
        Default chapter='1' (FAR). Use chapter='2' for DFARS (e.g., '252.227-7014').
    
        Auto-resolves the latest available date. Returns parsed clean text
        with heading, paragraphs, and citations.
    
        Common FAR sections: 2.101 (Definitions), 9.104-1 (Responsibility),
        15.305 (Proposal Evaluation), 19.502-2 (Small Business Set-Asides),
        52.212-4 (Commercial Terms), 52.212-5 (Required Commercial Terms).
        """
        section_id = _coerce_cfr_str(section_id, field="section_id", strip_prefixes=True)
        if not section_id:
            raise ValueError(
                "section_id is required. Pass a FAR/DFARS section like '15.305' or '52.212-4'. "
                f"Common sections: {', '.join(list(COMMON_FAR_SECTIONS.keys())[:5])}."
            )
        chapter = _validate_chapter(chapter, title_number=48)
        date = _validate_date_ymd(date, field="date")
        if date is None:
            date = await _resolve_date(48)
        return await get_cfr_content(
            title_number=48,
            date=date,
            chapter=chapter,
            section=section_id,
        )
  • The core workhorse function 'get_cfr_content' called by lookup_far_clause. Fetches XML from the eCFR API versioner endpoint and parses it into clean text (heading, paragraphs, citations). Validates title_number, date, section/part/subpart/chapter, auto-resolves the latest date, and returns parsed content.
    async def get_cfr_content(
        title_number: int = 48,
        date: str | None = None,
        part: Any = None,
        subpart: Any = None,
        section: Any = None,
        chapter: Any = None,
        raw_xml: bool = False,
    ) -> dict[str, Any]:
        """Get the full text of a CFR section, subpart, or part.
    
        This is the primary workhorse for reading regulatory text. Returns
        parsed clean text by default (heading, paragraphs, citations). Set
        raw_xml=True to get the original XML instead.
    
        Specify the narrowest scope possible to keep responses manageable:
        - section='15.305' for a single FAR section
        - subpart='15.3' for a subpart
        - part='15' for an entire part (can be large)
        - chapter='1' for an entire chapter (often >1 MB, avoid)
    
        Date auto-resolves to the latest available if not provided. Do NOT use
        today's date directly -- eCFR lags 1-2 business days and today often 404s.
    
        Title 48 = FAR/DFARS. Chapter 1 = FAR (Parts 1-99), Chapter 2 = DFARS
        (Parts 200-299). Other chapters = agency FAR supplements (GSAR, VAAR, etc.).
    
        For DFARS clauses, use chapter='2' (e.g., section='252.227-7014').
    
        part/subpart/section accept int or string. Common prefix mistakes like
        section='FAR 15.305' or '48 CFR 15.305' are stripped automatically.
        """
        title_number = _validate_title_number(title_number)
        date = _validate_date_ymd(date, field="date")
        section = _coerce_cfr_str(section, field="section", strip_prefixes=True)
        part = _coerce_cfr_str(part, field="part", strip_prefixes=True)
        subpart = _coerce_cfr_str(subpart, field="subpart", strip_prefixes=True)
        chapter = _validate_chapter(chapter, title_number=title_number)
    
        if not any((section, part, subpart, chapter)):
            raise ValueError(
                "get_cfr_content requires at least one of: section, subpart, part, chapter. "
                "Calling without any filter returns the entire title (often 20+ MB)."
            )
    
        if date is None:
            date = await _resolve_date(title_number)
    
        path = f"/api/versioner/v1/full/{date}/title-{title_number}.xml"
        params: dict[str, str] = {}
        if chapter:
            params["chapter"] = chapter
        if part:
            params["part"] = part
        if subpart:
            params["subpart"] = subpart
        if section:
            params["section"] = section
    
        xml_content = await _get_xml(path, params)
    
        if raw_xml:
            return {"date": date, "title": title_number, "xml": xml_content}
    
        parsed = _parse_xml_to_text(xml_content)
        parsed["date"] = date
        parsed["title"] = title_number
        if section:
            parsed["section"] = section
        if part:
            parsed["part"] = part
        if subpart:
            parsed["subpart"] = subpart
        if chapter:
            parsed["chapter"] = chapter
        return parsed
  • Helper '_resolve_date' called by lookup_far_clause when no date is provided. Queries the eCFR titles.json API to find the latest available date for Title 48.
    async def _resolve_date(title_number: int) -> str:
        """Resolve the latest available date for a CFR title.
    
        Called before any versioner endpoint. Using today's date often returns
        404 because eCFR lags 1-2 business days.
    
        Raises ValueError with an actionable message for reserved titles
        (which have null up_to_date_as_of) rather than building a URL with
        'None' in it.
        """
        data = await _get_json("/api/versioner/v1/titles.json")
        titles = _as_list(_safe_dict(data).get("titles"))
        for title in titles:
            t = _safe_dict(title)
            if _safe_int(t.get("number")) == title_number:
                utd = t.get("up_to_date_as_of")
                if not isinstance(utd, str) or not utd.strip():
                    reason = "this title is marked 'reserved'" if t.get("reserved") else (
                        "the API did not return up_to_date_as_of"
                    )
                    raise ValueError(
                        f"Cannot resolve a date for title {title_number}: {reason}. "
                        f"Reserved or un-issued titles have no published content."
                    )
                return utd
        raise ValueError(f"Title {title_number} not found in eCFR titles list.")
  • Helper '_coerce_cfr_str' normalizes section_id input (e.g., strips 'FAR ' or '48 CFR ' prefixes, accepts ints). Used by lookup_far_clause to clean up the section_id parameter.
    def _coerce_cfr_str(
        value: Any,
        *,
        field: str,
        strip_prefixes: bool = False,
        maxlen: int = 120,
    ) -> str | None:
        """Accept int or str for CFR identifiers (part/subpart/section/chapter).
    
        LLMs often pass ints (part=15). We coerce to str, strip whitespace, and
        optionally strip common user-added prefixes like 'FAR ' or '48 CFR '.
        Returns None for None/empty/whitespace-only. Raises on other types.
        """
        if value is None:
            return None
        if isinstance(value, bool):
            raise ValueError(f"{field} must be a string or integer, not bool.")
        if not isinstance(value, (str, int)):
            raise ValueError(
                f"{field} must be a string or integer. Got {type(value).__name__}."
            )
        raw = str(value)
        if "\x00" in raw:
            raise ValueError(f"{field}={value!r} contains a null byte.")
        if any(c in raw for c in ("\n", "\r", "\t")):
            raise ValueError(f"{field}={value!r} must not contain newline/tab characters.")
        s = raw.strip()
        if not s:
            return None
        if strip_prefixes:
            s = _SECTION_PREFIX_RE.sub("", s).strip()
            if not s:
                return None
        if len(s) > maxlen:
            raise ValueError(
                f"{field} exceeds maximum length of {maxlen} chars. Got {len(s)}."
            )
        return s
  • Helper '_validate_chapter' validates the chapter parameter against known Title 48 chapters. lookup_far_clause defaults chapter='1' (FAR) and validates it here.
    def _validate_chapter(value: Any, *, title_number: int | None = None) -> str | None:
        """Chapter is a string or int. If title=48, validate against TITLE_48_CHAPTERS."""
        s = _coerce_cfr_str(value, field="chapter", maxlen=8)
        if s is None:
            return None
        # For title 48 we know every legitimate chapter.
        if title_number == 48 and s not in TITLE_48_CHAPTERS:
            sample = ", ".join(list(TITLE_48_CHAPTERS.keys())[:10])
            raise ValueError(
                f"chapter={value!r} is not a valid Title 48 chapter. "
                f"Valid chapters: {sample} (see TITLE_48_CHAPTERS for full list). "
                f"Chapter 1=FAR, 2=DFARS."
            )
        return s
Behavior4/5

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

Annotations indicate readOnlyHint=true. The description adds that it auto-resolves the latest date and returns structured text (heading, paragraphs, citations), which is valuable beyond the annotation. No contradictions.

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 concise and front-loaded with the core purpose. It uses a clear structure with examples and default values. Could be slightly more polished but effectively communicates key info.

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 presence of an output schema, the description's mention of returned format is sufficient. It covers input parameters and usage scenarios well. A 4 is justified as it doesn't miss critical details.

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 coverage is 0%, so description must compensate. It explains section_id with examples and chapter default/usage. However, the 'date' parameter is only mentioned as 'auto-resolves the latest date', leaving ambiguity about whether a specific date can be passed. This gap lowers the score to 3.

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 tool looks up FAR/DFARS clause text with specific examples. It distinguishes from siblings like 'search_cfr' but doesn't explicitly contrast them, so a 4 is appropriate.

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

Usage Guidelines4/5

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

Provides clear instructions on how to pass section IDs and use the chapter parameter for FAR vs DFARS. Includes common examples. Lacks explicit 'when not to use' guidance but is otherwise strong.

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/1102tools/federal-contracting-mcps'

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