Classify Advisor data with FIBO
Many banks model their data with FIBO, the Financial Industry Business Ontology. This guide brings Advisor data into such a model already classified. In four calls it gets the regulators as FIBO individuals, an APRA standard typed with its FIBO class, its maker and the jurisdiction it is in force in, the standard's obligation rows linked to FIBO's legal obligation class, and a notification obligation found in the words of the AML/CTF Act.
Every call is one Advisor already answers, with fibo: true added. Without it the response is exactly what it was. The fibo block classifies a result; it adds no claim. FIBO is a trademark of the EDM Council.
Before you begin
- Python 3.10 or later and the official SDK:
pip install mcp. - A partner API key in
LAWATLAS_API_KEY, on an AI Pro account. See Get a credential.
Steps
- Get the regulators with
list_issuing_authoritiesandfibo: true. Each of the seven bodies the block names (APRA, ASIC, AUSTRAC, the RBA, the AASB, the AUASB and the ATO) comes with two IRIs:organisation, the body (the Australian Prudential Regulation Authority), androle, the regulatory role it plays (Australia's prudential regulator). FIBO models a regulator that way: the role makes the rules, and the role is played by the organisation. The Parliament comes with anorganisationand no role; other makers havenullwith a note. - Find the standard with
search_legislationandfibo: true. The title's block givestype(a legislative instrument is FIBO'sRegulation, an ActStatuteLaw),made_by(the regulator's role, or the Parliament for an Act) andis_in_force_in, which appears only while the title is in force and carries the sameconfirmed_with_frl_atas itsin_forceblock. - List its obligations with
list_obligationsandfibo: true. Each row'sfibo.expresseslinks it to a FIBO class byskos:closeMatch. A requirement or prohibition closely matchesLegalObligation; an offence, a benefit condition and a deeming provision haveclass: nullwith the reason. - Look up a notification obligation by its
obligation_id. A requirement whose words give a report or notice to a party, notify or inform a party, or report to, lodge with or serve on a party closely matchesNotificationObligation(rule OBL-13). The block quotes the words matched, names therecipientas the text names it, and givesnamed_in_text, the obligation's own name where the text gives one.
import asyncio
import json
import os
from mcp import ClientSession
from mcp.client.streamable_http import streamablehttp_client
URL = os.environ.get("LAWATLAS_MCP_URL", "https://advisor.lawatlas.ai/api/mcp")
class AdvisorError(Exception):
def __init__(self, error):
super().__init__(f"{error['code']}: {error['message']}")
self.code = error["code"]
async def call(session, tool, arguments):
"""Call one tool and return its JSON, raising on an error envelope."""
result = await session.call_tool(tool, arguments)
text = result.content[0].text
try:
body = json.loads(text)
except ValueError:
raise RuntimeError(text) # plain text: a lapsed subscription or a malformed argument
if isinstance(body, dict) and "error" in body:
raise AdvisorError(body["error"])
return body
def short(iri):
"""The last part of an IRI, for printing; store the whole IRI."""
return iri.rsplit("/", 1)[-1] if iri else None
async def classify(session, regulators, standard_code, topic, obligation_ids):
# 1. The regulators, as FIBO individuals: an organisation and the role it plays.
authorities = await call(session, "list_issuing_authorities", {"fibo": True})
header = authorities["fibo_alignment"]
print(f"FIBO release {header['release']}, namespace {header['namespace']}")
for a in authorities["items"]:
if a["code"] in regulators:
f = a["fibo"]
print(f" {a['code']}: organisation {f['organisation']}")
print(f" {' ' * len(a['code'])} role {f['role']}")
# 2. A standard, typed with FIBO's class for its kind, its maker and where it is in force.
found = await call(session, "search_legislation", {
"query": standard_code, "issuing_authority": [regulators[0]], "limit": 3, "fibo": True})
std = next(i for i in found["items"] if i.get("standard_code") == standard_code)
f = std["fibo"]
print(f"\n{standard_code} ({std['title_id']}): type {short(f['type'])} ({f['type_label']}), "
f"made by {short(f['made_by'])}")
if "is_in_force_in" in f:
print(f" in force in {short(f['is_in_force_in']['jurisdiction'])}, "
f"confirmed with the Federal Register at {f['is_in_force_in']['confirmed_with_frl_at']}")
# 3. Its obligation rows: each closely matches a FIBO class, never typed as one.
page = await call(session, "list_obligations", {
"title_id": std["title_id"], "topic": topic, "verified_only": True, "limit": 5, "fibo": True})
print(f"\nFirst {len(page['items'])} verified rows under headings containing {topic!r}:")
for r in page["items"]:
e = r["fibo"]["expresses"]
assert r["fibo"]["claim"] == page["claim"] # the row repeats the response's claim
print(f" {r['obligation_id']} {e['relation']} {e['class_label']} ({e['basis']})")
# 4. A notification obligation, found by rule OBL-13 in the provision's own words.
lookup = await call(session, "list_obligations", {"obligation_id": obligation_ids, "fibo": True})
for r in lookup["items"]: # `lookup` says each id's status; `items` holds the rows
e = r["fibo"]["expresses"]
print(f"\n{r['obligation_id']} ({r['pinpoint']}): {e['relation']} {e['class_label']}")
print(f" recipient: {e.get('recipient')}")
print(f" named: {e.get('named_in_text')}")
print(f" basis: {e['basis']}")
async def main():
headers = {"Authorization": f"Bearer {os.environ['LAWATLAS_API_KEY']}"}
async with streamablehttp_client(URL, headers=headers) as (read, write, _):
async with ClientSession(read, write) as session:
await session.initialize()
await classify(session, regulators=["APRA", "AUSTRAC"], standard_code="CPS 230",
topic="business continuity", obligation_ids=["C2006A00169/main/sec_41/GRC01"])
if __name__ == "__main__":
asyncio.run(main())
Check it worked
Run python fibo_classify.py. Run on 30 October 2026 against a local Advisor holding the same corpus, it printed:
FIBO release FIBO master_2026Q3, commit 33a92cd, namespace https://lawatlas.ai/ontology/fibo-au/
APRA: organisation https://lawatlas.ai/ontology/fibo-au/AURegulatoryAgencies/AustralianPrudentialRegulationAuthority
role https://lawatlas.ai/ontology/fibo-au/AURegulatoryAgencies/AustralianPrudentialRegulator
AUSTRAC: organisation https://lawatlas.ai/ontology/fibo-au/AURegulatoryAgencies/AustralianTransactionReportsAndAnalysisCentre
role https://lawatlas.ai/ontology/fibo-au/AURegulatoryAgencies/AustralianFinancialIntelligenceAndAMLRegulator
CPS 230 (F2026L00475): type Regulation (regulation), made by AustralianPrudentialRegulator
in force in AustralianJurisdiction, confirmed with the Federal Register at 2026-10-23T17:36:21.788051Z
First 5 verified rows under headings containing 'business continuity':
F2026L00475/main/para_33/GRC01 skos:closeMatch legal obligation (kind=requirement)
F2026L00475/main/para_33/GRC02 skos:closeMatch legal obligation (kind=requirement)
F2026L00475/main/para_33/GRC03 skos:closeMatch legal obligation (kind=requirement)
F2026L00475/main/para_33/GRC04 skos:closeMatch legal obligation (kind=requirement)
F2026L00475/main/para_33/GRC05 skos:closeMatch legal obligation (kind=requirement)
C2006A00169/main/sec_41/GRC01 (s 41(2)): skos:closeMatch notification obligation
recipient: the AUSTRAC CEO
named: suspicious matter reporting obligation
basis: kind=requirement; OBL-13 give_recipient_document: "must give the AUSTRAC CEO a report"
What to notice:
- The standard and the Act are typed differently. CPS 230 (F2026L00475) is a legislative instrument, so its type is
Regulation, made by APRA's role. The AML/CTF Act is an Act, made by the Parliament of Australia. - CPS 230's business continuity duties closely match
LegalObligation. Their words say what an entity must do, but give no report or notice to a party, so OBL-13 does not match them. That is not a finding that they are not notifications: a row OBL-13 does not match says nothing either way. - Section 41(2) closely matches
NotificationObligationbecause its words say "must give the AUSTRAC CEO a report". The recipient is the AUSTRAC CEO, as the Act names the office, and the obligation's own name, "suspicious matter reporting obligation", is the Act's. - Every row repeats the response's claim word for word (the script asserts it), so a row stored on its own still says what it claims.
Store it in your model
In the fibo block | What it is | How to store it |
|---|---|---|
type on a title | The FIBO class for the title's kind | The class of your node for the title |
made_by on a title | The maker as a FIBO individual: a regulator's role, or the Parliament | A link from the title to that IRI |
is_in_force_in on a title | The jurisdiction, and when the Federal Register last confirmed the status | A link to the jurisdiction, with the time beside it |
organisation, role on a regulator | The body, and the role it plays | Two individuals, linked as FIBO links a role to the organisation that plays it |
expresses on an obligation row | The closest FIBO class, by skos:closeMatch | skos:closeMatch from your node for the row to the class, never rdf:type |
recipient, named_in_text | The party and the obligation's name, as the text gives them | Text, as quoted |
A row is never typed as a FIBO obligation because it is a quoted provision whose text expresses an obligation, not the obligation itself, and not a statement of whom it applies to. Store the link as Advisor gives it.
The Australian individuals have been offered to the EDM Council for FIBO. Until they are merged their IRIs are in LawAtlas.ai's namespace, lawatlas.ai/ontology/fibo-au, and each one answers with Turtle when asked with Accept: text/turtle, with RDF/XML for Accept: application/rdf+xml, and with a page otherwise. The response's fibo_alignment names the namespace and the FIBO release the block was checked against. APRA's role, as the published Turtle defines it:
<https://lawatlas.ai/ontology/fibo-au/AURegulatoryAgencies/AustralianPrudentialRegulator>
a owl:NamedIndividual ;
a cmns-rga:RegulatoryAgency ;
rdfs:isDefinedBy <https://lawatlas.ai/ontology/fibo-au/AURegulatoryAgencies/> ;
rdfs:label "Australian prudential regulatory agency role of the Australian Prudential Regulation Authority" ;
skos:definition "regulatory agency role of the Australian Prudential Regulation Authority" ;
cmns-av:adaptedFrom "https://www.apra.gov.au"^^<http://www.w3.org/2001/XMLSchema#anyURI> ;
cmns-rlcmp:isPlayedBy la-au-rga:AustralianPrudentialRegulationAuthority .
What to do if it did not
| Problem | Solution |
|---|---|
KeyError: 'fibo' | The call was made without fibo: true; the block is only sent when asked for |
AdvisorError: FORBIDDEN from list_obligations | The key's organisation owner has no active AI Pro subscription; renew it |
StopIteration at step 2 | No title of that regulator carries that standard_code; try search_legislation with words from the standard's name |
| HTTP 401 | Check LAWATLAS_API_KEY; see Troubleshooting |
Next steps
list_obligations: the FIBO alignment section, and every argument, flag and error- Supply GRC data from Advisor: the same standard by paragraph, and staying current
- Cite Advisor output correctly