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

Output Schema

TableJSON Schema
NameRequiredDescriptionDefault
resultYes

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())
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 the tool's behavior: it's a search operation (implied read-only), returns 10-20 results with 18 key fields, and provides specific examples of how to use parameters. However, it doesn't mention potential limitations like rate limits, authentication requirements, or error conditions, which keeps it from a perfect score.

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, usage, parameters, examples, workflow, integration) and uses bold formatting effectively. While comprehensive, it's somewhat lengthy; every sentence serves a purpose, but some redundancy exists (e.g., repeating parameter lists in different sections). The front-loaded purpose and usage are clear, making it efficient for understanding.

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

Completeness5/5

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

Given the complexity (17 parameters, no annotations, 0% schema coverage) and the presence of an output schema, the description is remarkably complete. It explains the tool's purpose, when to use it, all parameters with semantics, provides examples, outlines a workflow, and describes cross-tool integration. The output schema handles return values, so the description appropriately focuses on usage context rather than output details.

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?

With 0% schema description coverage for 17 parameters, the description fully compensates by providing detailed parameter information. It clearly lists all 14 parameters (grouped as 9 minimal and 5 balanced), explains what each represents with examples (e.g., 'petition_type_code: Petition type (e.g., '551' = revival, '182' = restriction)'), and includes practical code examples showing parameter usage, adding substantial 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 performs a 'balanced search for Final Petition Decisions with comprehensive fields (10-20 results)', specifying both the action (search) and resource (Final Petition Decisions). It explicitly distinguishes from the sibling 'Search_petitions_minimal' by explaining this adds 5 advanced filters to the minimal tier, making the differentiation clear and specific.

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

Usage Guidelines5/5

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

The description provides explicit guidance on when to use this tool: 'Use for: Detailed petition analysis after minimal search, cross-referencing with PFW/PTAB data, analyzing petition types and legal context.' It also includes a 'Progressive Disclosure Workflow' section that shows how this tool fits into a sequence with the minimal search tool, and mentions cross-MCP integration alternatives, giving comprehensive usage context.

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

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