Get Charity Profile
charity_profileFetch a charity's full Commission profile including trustees, income, expenditure, and insolvency flags using its registration number. Use after charity_search to obtain the number.
Instructions
Fetch the full Charity Commission profile for a charity number.
Returns trustees, latest income/expenditure, insolvency flags, governing document type, classifications, and countries of operation. Use charity_search first to find the charity number.
Input Schema
| Name | Required | Description | Default |
|---|---|---|---|
| charity_number | Yes | Charity Commission registration number (e.g. '1234567'). Returned by charity_search. |
Output Schema
| Name | Required | Description | Default |
|---|---|---|---|
| charity_number | Yes | Charity registration number. | |
| charity_name | No | Registered charity name. | |
| reg_status | No | Registration status code ('R', 'RM'). | |
| reg_status_label | No | Human-readable registration status. | |
| charity_type | No | Charity type. | |
| charity_co_reg_number | No | Companies House number for charities also registered as companies (Charitable Incorporated Organisations, etc.). | |
| date_of_registration | No | Date of first registration. | |
| address | No | Registered address of the charity (joined address lines). | |
| latest_income | No | Latest filed annual income in GBP. | |
| latest_expenditure | No | Latest filed annual expenditure in GBP. | |
| insolvent | No | True if the charity is flagged as insolvent. | |
| in_administration | No | True if the charity is in administration. | |
| trustee_names | No | Trustees on record. Truncated to 30 entries. | |
| trustee_names_truncated | No | True if the trustee list was truncated. | |
| trustee_names_total | No | Total trustees upstream before truncation. | |
| who_what_where | No | Who/What/Where classification entries. The list may be truncated truncated to 50 entries. | |
| who_what_where_truncated | No | True if the classification list was truncated. | |
| who_what_where_total | No | Total classification entries upstream before truncation. | |
| countries_of_operation | No | Countries the charity operates in (capped at 10 upstream). |
Implementation Reference
- charity.py:127-136 (handler)Main tool handler for charity_profile. Registered with @mcp.tool(name='charity_profile'). Takes a charity_number and delegates to the shared fetch helper _fetch_charity_profile.
async def charity_profile( charity_number: Annotated[str, Field(description="Charity Commission registration number (e.g. '1234567'). Returned by charity_search.", min_length=1, max_length=20)], ) -> CharityProfile: """Fetch the full Charity Commission profile for a charity number. Returns trustees, latest income/expenditure, insolvency flags, governing document type, classifications, and countries of operation. Use charity_search first to find the charity number. """ return await _fetch_charity_profile(charity_number) - charity.py:145-204 (helper)Shared fetch helper _fetch_charity_profile - performs the actual API call to /allcharitydetailsV2/{lookup_number}/{suffix}, parses trustees, classifications, countries, income/expenditure, and returns a CharityProfile model.
async def _fetch_charity_profile(charity_number: str) -> CharityProfile: async with charity_client() as client: suffix = "0" lookup_number = charity_number if "-" in charity_number: parts = charity_number.split("-", 1) lookup_number = parts[0] suffix = parts[1] resp = await _request_with_retry( client, "GET", f"/allcharitydetailsV2/{lookup_number}/{suffix}", ) data = resp.json() reg_num = str(data.get("reg_charity_number") or lookup_number) raw_status = data.get("reg_status") raw_trustees = data.get("trustee_names") or [] trustees_total = len(raw_trustees) trustees = [ CharityTrustee(trustee_name=t.get("trustee_name")) for t in raw_trustees[:30] if isinstance(t, dict) ] raw_www = data.get("who_what_where") or [] www_total = len(raw_www) classifications = [ CharityClassification( classification_type=w.get("classification_type"), classification_desc=w.get("classification_desc"), ) for w in raw_www[:50] if isinstance(w, dict) ] countries_raw = data.get("CharityAoOCountryContinent") or [] countries = [c.get("country") for c in countries_raw[:10] if isinstance(c, dict) and c.get("country")] return CharityProfile( charity_number=reg_num, charity_name=data.get("charity_name"), reg_status=raw_status, reg_status_label=_STATUS_LABELS.get(raw_status or "", raw_status), charity_type=data.get("charity_type"), charity_co_reg_number=data.get("charity_co_reg_number") or None, date_of_registration=(data.get("date_of_registration") or "")[:10] or None, address=_build_address(data), latest_income=_coerce_number(data.get("latest_income")), latest_expenditure=_coerce_number(data.get("latest_expenditure")), insolvent=bool(data.get("insolvent", False)), in_administration=bool(data.get("in_administration", False)), trustee_names=trustees, trustee_names_truncated=trustees_total > 30, trustee_names_total=trustees_total, who_what_where=classifications, who_what_where_truncated=www_total > 50, who_what_where_total=www_total, countries_of_operation=countries, ) - models.py:540-611 (schema)CharityProfile Pydantic model defining the return type: charity_number, charity_name, reg_status, address, latest_income/expenditure, insolvency flags, trustees, classifications, and countries of operation.
class CharityProfile(BaseModel): """Full Charity Commission profile for a single charity.""" model_config = BASE_CFG charity_number: str = Field(..., description="Charity registration number.") charity_name: str | None = Field(None, description="Registered charity name.") reg_status: str | None = Field( None, description="Registration status code ('R', 'RM')." ) reg_status_label: str | None = Field( None, description="Human-readable registration status." ) charity_type: str | None = Field(None, description="Charity type.") charity_co_reg_number: str | None = Field( None, description=( "Companies House number for charities also registered as companies " "(Charitable Incorporated Organisations, etc.)." ), ) date_of_registration: str | None = Field( None, description="Date of first registration." ) address: str | None = Field( None, description="Registered address of the charity (joined address lines).", ) latest_income: float | None = Field( None, description="Latest filed annual income in GBP." ) latest_expenditure: float | None = Field( None, description="Latest filed annual expenditure in GBP." ) insolvent: bool = Field( False, description="True if the charity is flagged as insolvent." ) in_administration: bool = Field( False, description="True if the charity is in administration." ) trustee_names: list[CharityTrustee] = Field( default_factory=list, description=( "Trustees on record. Truncated to 30 entries." ), ) trustee_names_truncated: bool = Field( False, description="True if the trustee list was truncated." ) trustee_names_total: int = Field( 0, description="Total trustees upstream before truncation." ) who_what_where: list[CharityClassification] = Field( default_factory=list, description=( "Who/What/Where classification entries. The list may be truncated " "truncated to 50 entries." ), ) who_what_where_truncated: bool = Field( False, description="True if the classification list was truncated.", ) who_what_where_total: int = Field( 0, description="Total classification entries upstream before truncation.", ) countries_of_operation: list[str] = Field( default_factory=list, description="Countries the charity operates in (capped at 10 upstream).", ) - charity.py:51-117 (registration)Tool registration via @mcp.tool decorator on the charity_profile async function, registered inside charity.register_tools().
def register_tools(mcp: FastMCP) -> None: # ------------------------------------------------------------------ # # 1. charity_search # ------------------------------------------------------------------ # @mcp.tool( name="charity_search", annotations={ "title": "Search Charity Commission Register", "readOnlyHint": True, "destructiveHint": False, "idempotentHint": True, "openWorldHint": True, }, ) async def charity_search( query: Annotated[str, Field(description="Charity name or keyword to search for", min_length=2, max_length=200)], offset: Annotated[int, Field(description="Number of items to skip before this page. Default 0.", ge=0, le=10000)] = 0, limit: Annotated[int, Field(description="Max items to return in this page. Default 20; raise to 100 for bulk views.", ge=1, le=100)] = 20, ) -> CharitySearchResult: """Search the Charity Commission register of England and Wales by name or keyword. Returns matching charities with registration number, status, and registration date. Use charity_profile for full details once you have the charity number. The upstream `searchCharityName` endpoint returns the full list in one shot — pagination is applied client-side via offset/limit. """ async with charity_client() as client: resp = await _request_with_retry( client, "GET", f"/searchCharityName/{query}", ) data = resp.json() # API returns a list of charity objects directly all_charities = data if isinstance(data, list) else [] total = len(all_charities) page_slice = all_charities[offset : offset + limit] items: list[CharitySearchItem] = [] for raw in page_slice: raw_status = raw.get("reg_status") items.append( CharitySearchItem( charity_number=str(raw.get("reg_charity_number")) if raw.get("reg_charity_number") is not None else None, charity_name=raw.get("charity_name"), reg_status=raw_status, reg_status_label=_STATUS_LABELS.get(raw_status or "", raw_status), date_of_registration=(raw.get("date_of_registration") or "")[:10] or None, ) ) has_more = (offset + len(items)) < total return CharitySearchResult( query=query, total=total, offset=offset, limit=limit, returned=len(items), has_more=has_more, charities=items, ) @mcp.tool( - server.py:158-159 (registration)Top-level registration: charity.register_tools(mcp) and charity.register_resources(mcp) are called from server.py to wire up the charity_profile tool and resource.
companies_house.register_tools(mcp) charity.register_tools(mcp)