FCA Register MCP Server
A Model Context Protocol server that exposes the UK FCA Financial Services Register to LLM clients as a set of read-only tools. Built on FastMCP v3 and the fca-api async client.
Overview
- 25 read-only tools across five domains: firms, individuals, funds, regulated markets, and identifier search.
- Two transports: HTTP (FastAPI/uvicorn) for remote MCP clients, and stdio for local integrations.
- OAuth2 via Auth0, with two modes:
remote— JWT verification only (the MCP server trusts an upstream Auth0 tenant).proxy— full OAuth proxy with dynamic client registration; tokens are persisted to Azure Blob Storage, encrypted with Fernet.
- Scope-based authorization: tools tagged
fca_api:readrequire thefca-api:readscope in the access token. Enforcement is per-tool, soinitializeandtools/listremain reachable by unauthenticated clients. - Structured responses: Pydantic models generated by reflection from
fca-apitypes, stripping internal fields.
Tools
| Module | Tools |
|---|---|
search.py |
search_frn, search_irn, search_prn |
firms.py |
get_firm, get_firm_names, get_firm_addresses, get_firm_controlled_functions, get_firm_individuals, get_firm_permissions, get_firm_requirements, get_firm_requirement_investment_types, get_firm_regulators, get_firm_passports, get_firm_passport_permissions, get_firm_waivers, get_firm_exclusions, get_firm_disciplinary_history, get_firm_appointed_representatives |
individuals.py |
get_individual, get_individual_controlled_functions, get_individual_disciplinary_history |
funds.py |
get_fund, get_fund_names, get_fund_subfunds |
markets.py |
get_regulated_markets |
All tools are decorated with ToolAnnotations(readOnlyHint=True, destructiveHint=False, idempotentHint=True, openWorldHint=True).
Quick start
Prerequisites
- Python 3.13+
- PDM for dependency management
- FCA Register API credentials (
FCA_API_USERNAME,FCA_API_KEY) - An Auth0 tenant (only the
remotemode needs a tenant at minimum;proxymode additionally needs client credentials and Azure Blob Storage)
Install
pdm install
cp .env.example .env
# edit .env with your credentials
Run the HTTP server
pdm run python -m fca_mcp serve # production-like
pdm run python -m fca_mcp serve --reload # with autoreload
By default the server binds 0.0.0.0:8000. SERVER_BASE_URL must be set (used for OAuth metadata and resource URLs).
Run over stdio
pdm run python -m fca_mcp stdio
stdio mode skips the AuthMiddleware entirely — see server/__init__.py.
Docker
docker-compose up --build
docker-compose.yml launches the server alongside an Azurite emulator. Set FCA_API_USERNAME, FCA_API_KEY, AUTH0_DOMAIN, AUTH0_AUDIENCE, and SERVER_BASE_URL in the environment of the host running docker-compose.
Architecture
LLM client
│ MCP over HTTP (OAuth2 Bearer) or stdio
▼
┌─────────────────────────────────────────────┐
│ FastMCP server (fca_mcp.server.get_server) │
│ ─────────────────────────────────────────── │
│ Middleware stack (outer → inner): │
│ ErrorHandlingMiddleware │
│ RateLimitingMiddleware │
│ LoggingMiddleware │
│ AuthMiddleware (restrict_tag FCA_API_RO) │
│ ─────────────────────────────────────────── │
│ Sub-servers (mounted): │
│ search · firms · funds · │
│ individuals · markets │
│ ─────────────────────────────────────────── │
│ Auth provider: │
│ RemoteAuthProvider (mode=remote) │
│ Auth0Provider (mode=proxy) │
└───────────────────────┬─────────────────────┘
▼
fca_api.async_api.Client
▼
FCA Register REST API
Dependency injection
Tools receive the shared fca_api client through FastMCP's Depends chain defined in server/deps.py:
async def get_firm(
frn: FrnParam,
fca_client: fca_api.async_api.Client = deps.FcaApiDep,
) -> types.firm.FirmDetails:
result = await fca_client.get_firm(frn)
return types.firm.FirmDetails.from_api_t(result)
The client is constructed once in the server lifespan and reused across requests.
Type reflection
Response types are synthesised from fca-api types by reflect_fca_api_t() in server/types/base.py. Fields marked with InternalUrl are stripped. Tools convert raw results with Model.from_api_t(api_result).
Auth: scopes vs tags
Note the hyphen-vs-underscore distinction:
- Scope (
auth/scopes.py):FCA_API_RO = "fca-api:read"— the OAuth scope claimed in access tokens. - Tag (
auth/tags.py):FCA_API_RO = "fca_api:read"— the tag applied to tool decorators.
AuthMiddleware calls restrict_tag(FCA_API_RO, scopes=[FCA_API_RO]) — any tool tagged with fca_api:read requires the fca-api:read scope to execute. Untagged tools are allowed through without scope checks. On stdio, the middleware short-circuits.
Configuration
All settings are loaded from environment variables via Pydantic-Settings. See SETTINGS.md for the full reference and .env.example for a working template.
The most important variables:
| Variable | Required | Purpose |
|---|---|---|
FCA_API_USERNAME / FCA_API_KEY |
Yes | FCA Register API credentials |
AUTH0_MODE |
No (remote default) |
remote for JWT-only, proxy for OAuth proxy |
AUTH0_DOMAIN / AUTH0_AUDIENCE |
Yes | Auth0 tenant identifiers |
AUTH0_CLIENT_ID / AUTH0_CLIENT_SECRET / AUTH0_JWT_SIGNING_KEY / AUTH0_STORAGE_ENCRYPTION_KEY |
proxy mode only |
OAuth proxy secrets |
AUTH0_INTERACTIVE_CLIENT_ID |
No | When set, exposes the interactive /interactive web UI |
AZURE_CREDENTIAL |
proxy mode |
none (connection string / Azurite) or default (DefaultAzureCredential) |
AZURE_STORAGE_CONNECTION_STRING |
When AZURE_CREDENTIAL=none |
Connection string for Azurite or an Azure Storage account |
AZURE_STORAGE_ACCOUNT |
When AZURE_CREDENTIAL=default |
Storage account name |
SERVER_BASE_URL |
Yes | Public base URL used in OAuth resource metadata |
Health check
The HTTP app exposes GET /.container/health, returning service name, version, uptime, and timestamp. The Dockerfile HEALTHCHECK polls this endpoint.
Development
pdm run pytest # all tests with coverage
pdm run pytest tests/server/test_firm_simple.py # single file
pdm run pytest tests/server/test_firm_simple.py::test_get_firm -v
pdm run ruff check # lint
pdm run ruff check --fix # auto-fix
pdm run ruff format # format
Tests use FastMCP's in-memory FastMCPTransport — no HTTP server or live Auth0/Azure is required. Fixtures in tests/server/conftest.py replace the fca-api client with a record/replay mock and substitute a synthetic AccessToken so the auth middleware runs without live tokens. To test scope denial, override the oauth_scopes fixture to return [].
Code style
- Line length: 120
- Ruff rules:
A,B,C,E,F,I,W,N,C4,T20,PTH - Python 3.13+
Project layout
src/fca_mcp/
├── __main__.py # python -m fca_mcp → CLI
├── cli.py # typer CLI (serve / stdio)
├── settings.py # Pydantic-Settings config
├── logging.py # dictConfig-based logging setup
├── uvcorn_app.py # Starlette/uvicorn HTTP app factory
├── azure/ # Azure Blob key-value store + client factory
├── http/ # Interactive OAuth UI routes + static assets
└── server/
├── __init__.py # get_server(): mounts sub-servers + middleware
├── app.py # FcaApp lifespan container
├── deps.py # FastMCP Depends wiring
├── search.py # FRN/IRN/PRN identifier search
├── firms.py # 15 firm tools
├── funds.py # 3 fund tools
├── individuals.py # 3 individual tools
├── markets.py # get_regulated_markets
├── auth/ # Auth provider, scopes, tags
└── types/ # Reflected Pydantic models
License
MIT — see LICENSE.
Related
- fca-api — the underlying FCA Register REST client.
- FastMCP — MCP server framework.
- FCA Financial Services Register — upstream data source.
Metadata
Release files for fca-mcp 0.0.4
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | Provenance | Actions |
|---|---|---|---|---|
| fca_mcp-0.0.4.tar.gz | 34.7 kB | Without provenance | Details for fca_mcp-0.0.4.tar.gz |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Actions Reset |
|---|---|---|---|---|
| fca_mcp-0.0.4-py3-none-any.whl | Python 3 | none | any | Details for fca_mcp-0.0.4-py3-none-any.whl |
Total release size: 77.3 kB
Release files / fca_mcp-0.0.4.tar.gz
| Download URL | fca_mcp-0.0.4.tar.gz |
|---|---|
| Size | 34.7 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
a6f4c1b24168687e3e5192d95c3f37223c6ae3f347cdfb687ac5ba2166a32c52
|
|
BLAKE2b-256 checksum How to use checksums |
ba1f713458fc78ead5675a73371253d1ea0725696ddce5de46eddb79de46073e
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
pdm/2.26.8 CPython/3.13.13 Linux/6.17.0-1010-azure
|
Release files / fca_mcp-0.0.4-py3-none-any.whl
| Download URL | fca_mcp-0.0.4-py3-none-any.whl |
|---|---|
| Size | 42.7 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
5265ae408b046be136018dd9484976bebea722c213e02e66f2617ac568894296
|
|
BLAKE2b-256 checksum How to use checksums |
6abe78512cf957f3d1c0f8ef951956486958e012eb1f755fe0aba15315139d8b
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
pdm/2.26.8 CPython/3.13.13 Linux/6.17.0-1010-azure
|
Security for fca-mcp 0.0.4
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
No provenance found
None of the files in this release were uploaded with provenance metadata.
A note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.
To learn more about protecting yourself from malicious actors, visit our package security documentation.