search_company
Find French companies registered for e-invoicing by name, SIREN, or status. Returns SIREN, name, and registration details to enable 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:39-119 (handler)The MCP tool handler for 'search_company'. It is a FastMCP tool (registered via @mcp.tool() decorator) that accepts optional filters: name, siren, status, updated_after, and limit. It delegates to the DirectoryClient.search_company method.
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. """ client = get_directory_client() return await client.search_company( name=name, siren=siren, status=status, updated_after=updated_after, limit=limit, ) - clients/directory_client.py:49-68 (helper)The HTTP client method that performs the actual API call. Sends a POST request to /v1/siren/search with the provided filter parameters and returns the 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) return response.json() - tools/directory_tools.py:31-119 (registration)The register_directory_tools function registers all directory tools on the FastMCP instance. The 'search_company' tool is defined and registered via @mcp.tool() decorator inside this function (lines 38-119).
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. """ client = get_directory_client() return await client.search_company( name=name, siren=siren, status=status, updated_after=updated_after, limit=limit, ) - server.py:63-64 (registration)Top-level registration: register_directory_tools(mcp) is called in server.py to register all directory tools including 'search_company'.
register_flow_tools(mcp) register_directory_tools(mcp) - tools/directory_tools.py:39-89 (schema)Input schema for search_company defined via Pydantic Field annotations on the function parameters: name (optional str), siren (optional str), status (optional str), updated_after (optional str), limit (int, default=50, range 1-500).
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: