MCP server for the UK Financial Services (FCA) Register RESTful API
Project description
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.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file fca_mcp-0.0.3.tar.gz.
File metadata
- Download URL: fca_mcp-0.0.3.tar.gz
- Upload date:
- Size: 34.4 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: pdm/2.26.8 CPython/3.13.13 Linux/6.17.0-1010-azure
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
4fc8d9b2fdec946c5f696a387d5103d35d237c73844fae1471e39d1e15db544f
|
|
| MD5 |
dc54600d38419cddac461d272ec1049f
|
|
| BLAKE2b-256 |
004d0c3d2f4d42291cfe2f845ba325d93e0a1d990cd63ff4956d708da45c32b6
|
File details
Details for the file fca_mcp-0.0.3-py3-none-any.whl.
File metadata
- Download URL: fca_mcp-0.0.3-py3-none-any.whl
- Upload date:
- Size: 42.5 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: pdm/2.26.8 CPython/3.13.13 Linux/6.17.0-1010-azure
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
b07197c764ed0c4e7a60afc92f954831dd2a446002a02dbd2d28a00cb2c1e5fe
|
|
| MD5 |
7892553e68a73f428a387d974fb83298
|
|
| BLAKE2b-256 |
f912e183ac2727b796242d9494f50f3e3324b6eaa698a4652d8c3a4dd27e7af0
|