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 | |
|---|---|---|---|
| fca_mcp-0.0.4.tar.gz | 34.7 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| fca_mcp-0.0.4-py3-none-any.whl | Python 3 | none | any | Details |
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
|