Skip to main content
Glama
john-walkoe

USPTO Final Petition Decisions MCP Server

by john-walkoe

Search_petitions_balanced

Search USPTO final petition decisions with advanced filters for detailed analysis, cross-referencing with PFW/PTAB data, and examining petition types, art units, and legal context.

Instructions

Balanced search for Final Petition Decisions with comprehensive fields (10-20 results).

Balanced tier convenience parameters (14 total) - adds 5 advanced filters to minimal tier.

Use for: Detailed petition analysis after minimal search, cross-referencing with PFW/PTAB data, analyzing petition types and legal context. Returns: 18 key fields including petition type, art unit, technology center, prosecution status, legal issues, CFR rules cited, statutes cited, entity status, and invention title.

All Minimal Parameters (9) - same as Search_petitions_minimal:

  • applicant_name, application_number, patent_number

  • decision_type, deciding_office

  • petition_date_start/end, decision_date_start/end

Additional Balanced Parameters (5):

  • petition_type_code: Petition type (e.g., '551' = revival, '182' = restriction)

  • art_unit: Art unit number (e.g., '2128') - enables PFW cross-reference

  • technology_center: Tech center (e.g., '21', '2100')

  • prosecution_status: Status (e.g., 'During examination', 'Patented Case')

  • entity_status: Entity type (e.g., 'Small', 'Large', 'Undiscounted')

Examples:

# Revival petitions (type 551) that were denied
fpd_search_petitions_balanced(petition_type_code="551", decision_type="DENIED", limit=20)

# Complex combination for quality analysis
fpd_search_petitions_balanced(
    art_unit="2128", petition_type_code="551",
    decision_type="DENIED", prosecution_status="During examination", limit=20
)

Progressive Disclosure Workflow:

  1. Discovery: fpd_search_petitions_minimal(decision_type='DENIED', limit=100)

  2. User selects interesting petitions

  3. Analysis: fpd_search_petitions_balanced with advanced filters (art_unit, petition_type_code)

  4. Cross-reference: Use art_unit with PFW, use patentNumber with PTAB

Cross-MCP Integration:

  • applicationNumberText -> pfw_search_applications_minimal with fields parameter for targeted data

  • patentNumber -> ptab_search_proceedings_minimal(patent_number=X)

  • groupArtUnitNumber -> pfw_search_applications_minimal(art_unit=X, fields=[...])

  • firstApplicantName -> Match parties across PFW/PTAB MCPs

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
queryNo
limitNo
offsetNo
applicant_nameNo
application_numberNo
patent_numberNo
decision_typeNo
deciding_officeNo
petition_date_startNo
petition_date_endNo
decision_date_startNo
decision_date_endNo
petition_type_codeNo
art_unitNo
technology_centerNo
prosecution_statusNo
entity_statusNo

Implementation Reference

  • Main handler implementation for the 'Search_petitions_balanced' MCP tool. Builds Lucene query from 14 convenience parameters, performs search with 'petitions_balanced' field set (18 fields), filters response, adds query metadata and LLM guidance.
    @mcp.tool(name="Search_petitions_balanced")
    @async_tool_error_handler("balanced_search")
    async def fpd_search_petitions_balanced(
        query: str = "",
        limit: int = 10,
        offset: int = 0,
    
        # All 9 minimal tier parameters
        applicant_name: Optional[str] = None,
        application_number: Optional[str] = None,
        patent_number: Optional[str] = None,
        decision_type: Optional[str] = None,
        deciding_office: Optional[str] = None,
        petition_date_start: Optional[str] = None,
        petition_date_end: Optional[str] = None,
        decision_date_start: Optional[str] = None,
        decision_date_end: Optional[str] = None,
    
        # NEW: Balanced tier additional parameters (5 more)
        # Petition Classification
        petition_type_code: Optional[str] = None,     # e.g., "551" (revival), "182" (restriction)
        art_unit: Optional[str] = None,               # e.g., "2128", "3600"
        technology_center: Optional[str] = None,      # e.g., "21", "2100"
    
        # Status Filters
        prosecution_status: Optional[str] = None,     # e.g., "During examination", "Patented Case"
        entity_status: Optional[str] = None           # e.g., "Small", "Large", "Undiscounted"
    ) -> Dict[str, Any]:
        """Balanced search for Final Petition Decisions with comprehensive fields (10-20 results).
    
    **Balanced tier convenience parameters (14 total) - adds 5 advanced filters to minimal tier.**
    
    Use for: Detailed petition analysis after minimal search, cross-referencing with PFW/PTAB data,
    analyzing petition types and legal context.
    Returns: 18 key fields including petition type, art unit, technology center, prosecution status,
    legal issues, CFR rules cited, statutes cited, entity status, and invention title.
    
    **All Minimal Parameters (9) - same as Search_petitions_minimal:**
    - `applicant_name`, `application_number`, `patent_number`
    - `decision_type`, `deciding_office`
    - `petition_date_start/end`, `decision_date_start/end`
    
    **Additional Balanced Parameters (5):**
    - `petition_type_code`: Petition type (e.g., '551' = revival, '182' = restriction)
    - `art_unit`: Art unit number (e.g., '2128') - enables PFW cross-reference
    - `technology_center`: Tech center (e.g., '21', '2100')
    - `prosecution_status`: Status (e.g., 'During examination', 'Patented Case')
    - `entity_status`: Entity type (e.g., 'Small', 'Large', 'Undiscounted')
    
    **Examples:**
    ```python
    # Revival petitions (type 551) that were denied
    fpd_search_petitions_balanced(petition_type_code="551", decision_type="DENIED", limit=20)
    
    # Complex combination for quality analysis
    fpd_search_petitions_balanced(
        art_unit="2128", petition_type_code="551",
        decision_type="DENIED", prosecution_status="During examination", limit=20
    )
    ```
    
    **Progressive Disclosure Workflow:**
    1. Discovery: fpd_search_petitions_minimal(decision_type='DENIED', limit=100)
    2. User selects interesting petitions
    3. Analysis: fpd_search_petitions_balanced with advanced filters (art_unit, petition_type_code)
    4. Cross-reference: Use art_unit with PFW, use patentNumber with PTAB
    
    **Cross-MCP Integration:**
    - applicationNumberText -> pfw_search_applications_minimal with fields parameter for targeted data
    - patentNumber -> ptab_search_proceedings_minimal(patent_number=X)
    - groupArtUnitNumber -> pfw_search_applications_minimal(art_unit=X, fields=[...])
    - firstApplicantName -> Match parties across PFW/PTAB MCPs"""
        try:
            # Input validation
            if limit < 1 or limit > 50:
                return format_error_response("Limit must be between 1 and 50", 400)
            if offset < 0:
                return format_error_response("Offset must be non-negative", 400)
    
            # Build query from convenience parameters
            try:
                final_query, convenience_params_used = _build_convenience_query(
                    query=query,
                    applicant_name=applicant_name,
                    application_number=application_number,
                    patent_number=patent_number,
                    decision_type=decision_type,
                    deciding_office=deciding_office,
                    petition_date_start=petition_date_start,
                    petition_date_end=petition_date_end,
                    decision_date_start=decision_date_start,
                    decision_date_end=decision_date_end,
                    petition_type_code=petition_type_code,
                    art_unit=art_unit,
                    technology_center=technology_center,
                    prosecution_status=prosecution_status,
                    entity_status=entity_status,
                    allow_balanced_params=True  # Balanced tier allows all
                )
            except ValueError as e:
                return format_error_response(str(e), 400)
    
            # Additional query length validation
            if len(final_query) > 2000:
                return format_error_response("Combined query too long (max 2000 characters)", 400)
    
            # Get fields from field manager
            fields = field_manager.get_fields("petitions_balanced")
    
            # Search petitions
            result = await api_client.search_petitions(
                query=final_query,
                fields=fields,
                limit=limit,
                offset=offset
            )
    
            # Check for errors
            if "error" in result:
                return result
    
            # Filter response using field manager
            filtered_result = field_manager.filter_response(result, "petitions_balanced")
    
            # Add query metadata
            filtered_result["query_info"] = {
                "final_query": final_query,
                "convenience_parameters_used": convenience_params_used,
                "tier": "balanced",
                "available_parameters": [
                    "applicant_name", "application_number", "patent_number",
                    "decision_type", "deciding_office",
                    "petition_date_start", "petition_date_end",
                    "decision_date_start", "decision_date_end",
                    "petition_type_code", "art_unit", "technology_center",
                    "prosecution_status", "entity_status"
                ]
            }
    
            # Add enhanced usage guidance
            filtered_result["llm_guidance"] = {
                "workflow": "Balanced Analysis -> Cross-MCP Integration -> Document Retrieval",
                "cross_mcp_workflows": {
                    "pfw_prosecution": "pfw_search_applications_minimal with fields parameter for examiner/status context",
                    "ptab_challenges": "ptab_search_proceedings_minimal(patent_number=X) if patentNumber present",
                    "art_unit_analysis": "fpd_search_petitions_by_art_unit(art_unit=X) for pattern analysis"
                },
                "red_flags": {
                    "revival_37cfr1137": "Application abandoned - revival petition filed",
                    "dispute_37cfr1181": "Examiner conflict - supervisory review petition",
                    "denied_petition": "Director denied - weak arguments or procedural errors"
                },
                "next_steps": [
                    "fpd_get_petition_details for full details + documents",
                    "Cross-reference applicationNumberText with PFW",
                    "Cross-reference patentNumber with PTAB",
                    "Use fpd_search_petitions_by_art_unit for examiner patterns"
                ]
            }
    
            return filtered_result
    
        except ValueError as e:
            logger.warning(f"Validation error in balanced search: {str(e)}")
            return format_error_response(str(e), 400)
        except httpx.HTTPStatusError as e:
            logger.error(f"API error in balanced search: {e.response.status_code} - {e.response.text}")
            return format_error_response(f"API error: {e.response.text}", e.response.status_code)
        except httpx.TimeoutException as e:
            logger.error(f"API timeout in balanced search: {str(e)}")
            return format_error_response("Request timeout - please try again", 408)
        except Exception as e:
            logger.error(f"Unexpected error in balanced search: {str(e)}")
            return format_error_response(f"Internal error: {str(e)}", 500)
  • MCP tool registration decorator defining the tool name as 'Search_petitions_balanced'.
    @mcp.tool(name="Search_petitions_balanced")
  • Field configuration schema for 'petitions_balanced' set used by the tool, defining the 18 fields returned in responses for context reduction.
    "petitions_balanced": {
        "description": "Key fields for petition analysis",
        "fields": [
            "petitionDecisionRecordIdentifier",
            "applicationNumberText",
            "patentNumber",
            "firstApplicantName",
            "decisionTypeCodeDescriptionText",
            "petitionMailDate",
            "decisionDate",
            "finalDecidingOfficeName",
            "decisionPetitionTypeCode",
            "decisionPetitionTypeCodeDescriptionText",
            "groupArtUnitNumber",
            "technologyCenter",
            "businessEntityStatusCategory",
            "prosecutionStatusCodeDescriptionText",
            "inventionTitle",
            "petitionIssueConsideredTextBag",
            "ruleBag",
            "statuteBag"
        ]
  • Helper function to construct the search query from convenience parameters (applicant_name, art_unit, etc.), used by both minimal and balanced search tools.
    def _build_convenience_query(
        query: str = "",
        # Core Identity & Party
        applicant_name: Optional[str] = None,
        application_number: Optional[str] = None,
        patent_number: Optional[str] = None,
        # Decision Filters
        decision_type: Optional[str] = None,
        deciding_office: Optional[str] = None,
        # Date Ranges
        petition_date_start: Optional[str] = None,
        petition_date_end: Optional[str] = None,
        decision_date_start: Optional[str] = None,
        decision_date_end: Optional[str] = None,
        # Balanced tier additional parameters
        petition_type_code: Optional[str] = None,
        art_unit: Optional[str] = None,
        technology_center: Optional[str] = None,
        prosecution_status: Optional[str] = None,
        entity_status: Optional[str] = None,
        # Control which parameters are allowed
        allow_balanced_params: bool = False
    ) -> tuple[str, dict]:
        """Build query string from convenience parameters
    
        Returns:
            tuple: (final_query_string, convenience_parameters_used)
        """
        try:
            # Build query from convenience parameters
            query_parts = []
            convenience_params_used = {}
    
            # Include base query if provided
            if query and query.strip():
                query_parts.append(f"({query})")
                convenience_params_used["base_query"] = query
    
            # Add minimal tier convenience parameters
            if applicant_name:
                validated_name = validate_string_param("applicant_name", applicant_name)
                if validated_name:
                    query_parts.append(f'{QueryFieldNames.APPLICANT_NAME}:"{validated_name}"')
                    convenience_params_used["applicant_name"] = validated_name
    
            if application_number:
                validated_app = validate_application_number(application_number)
                if validated_app:
                    query_parts.append(f"{QueryFieldNames.APPLICATION_NUMBER}:{validated_app}")
                    convenience_params_used["application_number"] = validated_app
    
            if patent_number:
                validated_patent = validate_string_param("patent_number", patent_number, 15)
                if validated_patent:
                    query_parts.append(f"{QueryFieldNames.PATENT_NUMBER}:{validated_patent}")
                    convenience_params_used["patent_number"] = validated_patent
    
            if decision_type:
                validated_decision = validate_string_param("decision_type", decision_type, 50)
                if validated_decision:
                    query_parts.append(f"{QueryFieldNames.DECISION_TYPE}:{validated_decision}")
                    convenience_params_used["decision_type"] = validated_decision
    
            if deciding_office:
                validated_office = validate_string_param("deciding_office", deciding_office)
                if validated_office:
                    query_parts.append(f'{FPDFields.FINAL_DECIDING_OFFICE_NAME}:"{validated_office}"')
                    convenience_params_used["deciding_office"] = validated_office
    
            # Date range filters
            if petition_date_start or petition_date_end:
                start = validate_date_range(petition_date_start) if petition_date_start else "*"
                end = validate_date_range(petition_date_end) if petition_date_end else "*"
                if start != "*" or end != "*":
                    query_parts.append(f"{QueryFieldNames.PETITION_MAIL_DATE}:[{start} TO {end}]")
                    convenience_params_used["petition_date_range"] = f"{start} TO {end}"
    
            if decision_date_start or decision_date_end:
                start = validate_date_range(decision_date_start) if decision_date_start else "*"
                end = validate_date_range(decision_date_end) if decision_date_end else "*"
                if start != "*" or end != "*":
                    query_parts.append(f"{QueryFieldNames.DECISION_DATE}:[{start} TO {end}]")
                    convenience_params_used["decision_date_range"] = f"{start} TO {end}"
    
            # Add balanced tier additional parameters (only if allowed)
            if allow_balanced_params:
                if petition_type_code:
                    validated_type = validate_string_param("petition_type_code", petition_type_code, 10)
                    if validated_type:
                        query_parts.append(f"{FPDFields.DECISION_PETITION_TYPE_CODE}:{validated_type}")
                        convenience_params_used["petition_type_code"] = validated_type
    
                if art_unit:
                    validated_art_unit = validate_string_param("art_unit", art_unit, 10)
                    if validated_art_unit:
                        query_parts.append(f"{QueryFieldNames.ART_UNIT}:{validated_art_unit}")
                        convenience_params_used["art_unit"] = validated_art_unit
    
                if technology_center:
                    validated_tc = validate_string_param("technology_center", technology_center, 10)
                    if validated_tc:
                        query_parts.append(f"{QueryFieldNames.TECHNOLOGY_CENTER}:{validated_tc}")
                        convenience_params_used["technology_center"] = validated_tc
    
                if prosecution_status:
                    validated_status = validate_string_param("prosecution_status", prosecution_status)
                    if validated_status:
                        query_parts.append(f'{QueryFieldNames.PROSECUTION_STATUS}:"{validated_status}"')
                        convenience_params_used["prosecution_status"] = validated_status
    
                if entity_status:
                    validated_entity = validate_string_param("entity_status", entity_status, 50)
                    if validated_entity:
                        query_parts.append(f'{QueryFieldNames.BUSINESS_ENTITY}:"{validated_entity}"')
                        convenience_params_used["entity_status"] = validated_entity
            else:
                # Check if balanced-only parameters were provided but not allowed
                balanced_only_params = [petition_type_code, art_unit, technology_center, prosecution_status, entity_status]
                provided_balanced_params = [p for p in balanced_only_params if p is not None]
                if provided_balanced_params:
                    raise ValidationError(
                        "Parameters petition_type_code, art_unit, technology_center, prosecution_status, "
                        "and entity_status are only available in fpd_search_petitions_balanced. "
                        "Use fpd_search_petitions_balanced for advanced filtering.",
                        generate_request_id()
                    )
    
            # Validate we have at least one search criterion
            if not query_parts:
                raise ValidationError(
                    "Must provide either 'query' parameter or at least one convenience parameter",
                    generate_request_id()
                )
    
            # Combine all query parts with AND
            final_query = " AND ".join(query_parts)
    
            return final_query, convenience_params_used
    
        except ValidationError:
            # Re-raise ValidationError as-is
            raise
        except Exception as e:
            raise ValidationError(f"Query building failed: {str(e)}", generate_request_id())

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/john-walkoe/uspto_fpd_mcp'

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