search_company
Search for French companies in the PPF e-invoicing directory by name, SIREN, or status to find active legal units for invoice routing.
Instructions
Search for companies (legal units / SIRENs) in the PPF directory by criteria.
Returns VAT-registered French legal units recorded in the PPF directory. A company must appear here before its establishments (SIRETs) or directory lines can be used.
BEHAVIOR:
Returns a paginated list of matching companies; empty list if none match.
At least one search criterion must be provided; omitting all returns an error.
Name search is a partial, case-insensitive match against the legal name and trade name.
Pagination: if the response contains 'nextUpdatedAfter', pass it as updated_after to get the next page.
RESPONSE: each item includes siren, name, status (Active/Inactive/Pending), approvedPlatformId, and timestamps (createdAt, updatedAt).
USAGE GUIDELINES:
Prefer get_company_by_siren when you already know the exact SIREN (faster, direct lookup).
Use search_company with name to resolve a company name to its SIREN before further lookups.
Always check status == Active before attempting to send invoices to or look up establishments for a company.
A company not present in the directory is not yet registered for e-invoicing; invoices cannot be routed to it.
After finding the SIREN, call search_establishment or get_directory_line to find the recipient's address.
Input Schema
| Name | Required | Description | Default |
|---|---|---|---|
| name | No | Company name or trade name (partial match accepted). Example: 'Dupont' returns all entities whose name contains 'Dupont'. Use when you know the name but not the SIREN. | |
| siren | No | Company SIREN number (9 digits, no spaces). Example: '123456789'. Use for an exact lookup; prefer get_company_by_siren when the SIREN is known. | |
| status | No | Registration status of the legal unit in the PPF directory. Active: registered and reachable for e-invoicing. Inactive: deregistered; cannot receive invoices. Pending: registration in progress. | |
| updated_after | No | Pagination cursor: only return entries updated after this date/time (ISO 8601, e.g. 2024-09-01T00:00:00Z). Use the 'nextUpdatedAfter' field from the previous response to fetch the next page. | |
| limit | No | Maximum number of results per page (1-500, default 50). |
Output Schema
| Name | Required | Description | Default |
|---|---|---|---|
No arguments | |||
Implementation Reference
- tools/directory_tools.py:78-163 (handler)MCP tool handler for search_company: decorated with @mcp.tool(), defines schema (name, siren, status, updated_after, limit), validates SIREN if provided, calls DirectoryClient.search_company(), and returns the result.
async def search_company( name: Annotated[ Optional[str], Field( default=None, description=( "Company name or trade name (partial match accepted). " "Example: 'Dupont' returns all entities whose name contains 'Dupont'. " "Use when you know the name but not the SIREN." ), ), ] = None, siren: Annotated[ Optional[str], Field( default=None, description=( "Company SIREN number (9 digits, no spaces). " "Example: '123456789'. " "Use for an exact lookup; prefer get_company_by_siren when the SIREN is known." ), ), ] = None, status: Annotated[ Optional[str], Field( default=None, description=( "Registration status of the legal unit in the PPF directory. " "Active: registered and reachable for e-invoicing. " "Inactive: deregistered; cannot receive invoices. " "Pending: registration in progress." ), ), ] = None, updated_after: Annotated[ Optional[str], Field( default=None, description=( "Pagination cursor: only return entries updated after this date/time " "(ISO 8601, e.g. 2024-09-01T00:00:00Z). " "Use the 'nextUpdatedAfter' field from the previous response to fetch the next page." ), ), ] = None, limit: Annotated[ int, Field(default=50, ge=1, le=500, description="Maximum number of results per page (1-500, default 50)."), ] = 50, ) -> dict: """ Search for companies (legal units / SIRENs) in the PPF directory by criteria. Returns VAT-registered French legal units recorded in the PPF directory. A company must appear here before its establishments (SIRETs) or directory lines can be used. BEHAVIOR: - Returns a paginated list of matching companies; empty list if none match. - At least one search criterion must be provided; omitting all returns an error. - Name search is a partial, case-insensitive match against the legal name and trade name. - Pagination: if the response contains 'nextUpdatedAfter', pass it as updated_after to get the next page. RESPONSE: each item includes siren, name, status (Active/Inactive/Pending), approvedPlatformId, and timestamps (createdAt, updatedAt). USAGE GUIDELINES: - Prefer get_company_by_siren when you already know the exact SIREN (faster, direct lookup). - Use search_company with name to resolve a company name to its SIREN before further lookups. - Always check status == Active before attempting to send invoices to or look up establishments for a company. - A company not present in the directory is not yet registered for e-invoicing; invoices cannot be routed to it. - After finding the SIREN, call search_establishment or get_directory_line to find the recipient's address. """ if siren is not None: try: siren = _validate_siren(siren) except ValueError as exc: return {"error": str(exc)} client = get_directory_client() return await client.search_company( name=name, siren=siren, status=status, updated_after=updated_after, limit=limit, ) - tools/directory_tools.py:78-128 (schema)Input schema for search_company defined via Pydantic Field annotations: name (Optional[str], partial match), siren (Optional[str], 9 digits), status (Optional[str], Active/Inactive/Pending), updated_after (Optional[str], ISO 8601 cursor), limit (int, 1-500, default 50).
async def search_company( name: Annotated[ Optional[str], Field( default=None, description=( "Company name or trade name (partial match accepted). " "Example: 'Dupont' returns all entities whose name contains 'Dupont'. " "Use when you know the name but not the SIREN." ), ), ] = None, siren: Annotated[ Optional[str], Field( default=None, description=( "Company SIREN number (9 digits, no spaces). " "Example: '123456789'. " "Use for an exact lookup; prefer get_company_by_siren when the SIREN is known." ), ), ] = None, status: Annotated[ Optional[str], Field( default=None, description=( "Registration status of the legal unit in the PPF directory. " "Active: registered and reachable for e-invoicing. " "Inactive: deregistered; cannot receive invoices. " "Pending: registration in progress." ), ), ] = None, updated_after: Annotated[ Optional[str], Field( default=None, description=( "Pagination cursor: only return entries updated after this date/time " "(ISO 8601, e.g. 2024-09-01T00:00:00Z). " "Use the 'nextUpdatedAfter' field from the previous response to fetch the next page." ), ), ] = None, limit: Annotated[ int, Field(default=50, ge=1, le=500, description="Maximum number of results per page (1-500, default 50)."), ] = 50, ) -> dict: - clients/directory_client.py:57-78 (helper)DirectoryClient.search_company() — the actual HTTP client method that POSTs to /v1/siren/search with the filter criteria and returns JSON response.
async def search_company( self, name: Optional[str] = None, siren: Optional[str] = None, status: Optional[str] = None, updated_after: Optional[str] = None, limit: int = 50, ) -> dict[str, Any]: """POST /v1/siren/search — Search legal units in the PPF directory.""" body: dict[str, Any] = {"limit": limit} if name: body["name"] = name if siren: body["siren"] = siren if status: body["status"] = status if updated_after: body["updatedAfter"] = updated_after response = await self._request("POST", "/v1/siren/search", json=body) if response.status_code == 204: return {"total": 0} return response.json() - tools/directory_tools.py:70-163 (registration)Tool registration: search_company is registered inside register_directory_tools() using the @mcp.tool() decorator.
def register_directory_tools(mcp: FastMCP) -> None: """Registers the 12 Directory Service tools on the FastMCP instance.""" # ------------------------------------------------------------------ # SIREN — Legal units # ------------------------------------------------------------------ @mcp.tool() async def search_company( name: Annotated[ Optional[str], Field( default=None, description=( "Company name or trade name (partial match accepted). " "Example: 'Dupont' returns all entities whose name contains 'Dupont'. " "Use when you know the name but not the SIREN." ), ), ] = None, siren: Annotated[ Optional[str], Field( default=None, description=( "Company SIREN number (9 digits, no spaces). " "Example: '123456789'. " "Use for an exact lookup; prefer get_company_by_siren when the SIREN is known." ), ), ] = None, status: Annotated[ Optional[str], Field( default=None, description=( "Registration status of the legal unit in the PPF directory. " "Active: registered and reachable for e-invoicing. " "Inactive: deregistered; cannot receive invoices. " "Pending: registration in progress." ), ), ] = None, updated_after: Annotated[ Optional[str], Field( default=None, description=( "Pagination cursor: only return entries updated after this date/time " "(ISO 8601, e.g. 2024-09-01T00:00:00Z). " "Use the 'nextUpdatedAfter' field from the previous response to fetch the next page." ), ), ] = None, limit: Annotated[ int, Field(default=50, ge=1, le=500, description="Maximum number of results per page (1-500, default 50)."), ] = 50, ) -> dict: """ Search for companies (legal units / SIRENs) in the PPF directory by criteria. Returns VAT-registered French legal units recorded in the PPF directory. A company must appear here before its establishments (SIRETs) or directory lines can be used. BEHAVIOR: - Returns a paginated list of matching companies; empty list if none match. - At least one search criterion must be provided; omitting all returns an error. - Name search is a partial, case-insensitive match against the legal name and trade name. - Pagination: if the response contains 'nextUpdatedAfter', pass it as updated_after to get the next page. RESPONSE: each item includes siren, name, status (Active/Inactive/Pending), approvedPlatformId, and timestamps (createdAt, updatedAt). USAGE GUIDELINES: - Prefer get_company_by_siren when you already know the exact SIREN (faster, direct lookup). - Use search_company with name to resolve a company name to its SIREN before further lookups. - Always check status == Active before attempting to send invoices to or look up establishments for a company. - A company not present in the directory is not yet registered for e-invoicing; invoices cannot be routed to it. - After finding the SIREN, call search_establishment or get_directory_line to find the recipient's address. """ if siren is not None: try: siren = _validate_siren(siren) except ValueError as exc: return {"error": str(exc)} client = get_directory_client() return await client.search_company( name=name, siren=siren, status=status, updated_after=updated_after, limit=limit, )