This document explains how pyegeria turns Egeria responses into useful output using report specs (a.k.a. format sets). It covers available output formats, the report spec model, how nested/master–detail views work, and how to create and load your own specs.
If you are new to pyegeria, start with the README for installation and configuration, then return here when you need to tailor the output of find/get operations.
- Show a user profile with master–detail output (table + linked detail sections):
from pyegeria.omvs.my_profile import MyProfile
client = MyProfile("qs-view-server", "https://localhost:9443", user_id="user", user_pwd="secret")
client.create_egeria_bearer_token("user", "secret")
md = client.get_my_profile(output_format="LIST", report_spec="My-User-MD")
print(md) # Markdown table with [details] links for Contact Methods, Roles, Teams, Communities- Get the same data as a nested dictionary (ideal for programmatic processing/UIs):
profile = client.get_my_profile(output_format="DICT", report_spec="My-User-MD")
# dict with scalar attributes + lists of dicts for contact_methods, roles, teams, communitiespyegeria supports multiple output types. Each report spec declares which types it supports.
- DICT: Python list/dict structures — best for programmatic use and tests. This format provides a "materialized" view where properties are cleaned up and nested elements are promoted to logical keys.
- JSON: Raw Egeria response dictionary — ideal for advanced users who need to process the exact response from the Egeria platform without any pyegeria-specific transformations.
- LIST: Markdown table (horizontal). Good for compact overviews. Nested values are automatically summarized (names/display names). If a
detail_specis configured, master rows include[details]links to rich detail sections appended below the table. - REPORT: Rich Markdown (vertical). Ideal for deep dives. Renders nested dict/list values as hierarchical bullet lists and includes vertical detail sections for master-detail columns.
- REPORT-GRAPH: Recursive, linked Markdown report. Builds anchor-linked sections per element (by GUID), and adds link lists for peer and child/nested elements; recurses while avoiding cycles.
- FORM: Markdown suitable for Dr.Egeria editable forms. Complex values are summarized to keep the form concise and manageable.
- MD: Plain Markdown (legacy simple rendering).
- MERMAID: Mermaid graph text for supported responses.
- HTML: HTML wrapper around Markdown (when enabled in generators).
Tip: Use DICT for APIs and automation; use LIST for dashboards/browsing; use REPORT for deep, readable details; use FORM when producing updateable forms.
All find_* and get_* methods in pyegeria that support formatted output follow a consistent parameter pattern:
output_format: The desired output type (e.g., "DICT", "LIST", "REPORT"). Defaults to "DICT" or "JSON" depending on the method.report_spec: Optional name or dictionary defining the columns and layout.search_string/filter_string:find_*methods usesearch_stringfor substring matching.get_*methods usefilter_stringfor exact value matching.- Normalization: The output layer automatically handles both. Whichever string was used to fetch the data will be displayed in the output preamble (e.g., in Markdown reports).
**kwargs: Methods accept additional keyword arguments which are passed safely through to the output formatter. This allows for future rendering flags without breaking method signatures.
- For method/CLI parameters and action specs, prefer snake_case names (e.g.,
metadata_element_subtypes,page_size,start_from,effective_time,sequencing_order). - The report executor (
exec_report_spec/run_report) and CLI adaptors expect snake_case; underlying clients convert to on‑wire camelCase when calling Egeria. - Column
keynames in report specs can be snake_case or camelCase. The formatter tries exact key, thento_camel_case, then uppercase for well‑known IDs. Prefer snake_case for consistency. - CLI flags like
--param page_size=50or--params-json '{"metadata_element_subtypes":["Asset"],"page_size":100}'must use snake_case.
Report specs are defined using Pydantic models in pyegeria/view/_output_format_models.py and registered in pyegeria/view/base_report_formats.py.
- FormatSet
target_type: logical type (e.g., "Glossary", "Project", "My-User")heading,description, optionalfamily, optionalaliasesformats: a list ofFormatentries- optional
action: how to call a client method (used by higher-level tooling)
- Format
types: list of output types this format supports (e.g.,["DICT", "LIST"])attributes: list ofColumn(alias:Attribute)
- Column (Attribute)
name: display name in the outputkey: property key to extract from the data (snake_case or camelCase supported)format: optional flag to apply formatting suitable for tables/linksdetail_spec: optional name of another report spec to render a nested detail section (master–detail)
Example (Python) — simplified snippet:
from pyegeria.view._output_format_models import Column, Format, FormatSet
My_User_MD = FormatSet(
target_type="My-User",
heading="My Information Master-Detail",
description="User Information with links to details",
family="MyProfile",
formats=[
Format(
types=["DICT", "LIST", "REPORT"],
attributes=[
Column(name="Full Name", key="full_name"),
Column(name="User ID", key="user_id"),
Column(name="Roles", key="roles", detail_spec="My-User-Roles-Detail"),
],
)
],
)Most client methods already call the output pipeline for you. Under the hood, the following helpers are used (in pyegeria/view/output_formatter.py and pyegeria/view/base_report_formats.py):
select_report_format(label: str, output_type: str) -> dict: pick a spec by name/label and match the requested type (with fallback toALL).resolve_output_formats(entity_type: str, output_format: str, report_spec: str|dict|None): flexible resolver used by clients (by name, by dict, or by entity type default).generate_output(...): orchestrates DICT/LIST/REPORT/FORM rendering given elements and a spec. It supports aninclude_preambleparameter (defaultTrue) to control whether the report header/preamble is included; this is automatically disabled during recursive master-detail calls to prevent redundant headings.
When you request an output_format, pyegeria selects the best matching Format inside the formats list for the chosen spec:
- Exact match: a
Formatwhosetypescontains the requested type (e.g.,"LIST"). - Fallback to
ALL: if no exact match is found, aFormatwhosetypesincludes"ALL"will be used. - Final fallback: if neither is present, the first
Formatentry is used. - Precedence: if both a type‑specific
Formatand anALLFormatare present, the type‑specific one wins.
Example JSON using ALL:
{
"My-User-Compact": {
"target_type": "My-User",
"formats": [
{"types": ["ALL"], "attributes": [
{"name":"Name","key":"display_name"},
{"name":"GUID","key":"guid","format":true}
]}
]
}
}This single spec works for DICT, LIST, and REPORT with the same attribute set. You can add another Format with types: ["REPORT"] later if you want a richer vertical report while keeping ALL for other types.
Basic example using a registered spec:
from pyegeria.view.base_report_formats import select_report_format
from pyegeria.view.output_formatter import generate_output
spec = select_report_format("My-User-MD", "LIST")
md = generate_output(elements=[element], search_string="", entity_type="My-User", output_format="LIST",
extract_properties_func=my_extractor, columns_struct=spec)In practice you will call a client method (e.g., MyProfile.get_my_profile) and pass output_format + report_spec.
Egeria OMVS responses often include related elements and nested hierarchies. pyegeria preserves this richness and makes it navigable across all primary output formats (LIST, REPORT, DICT, and CLI TABLE).
- Rich materialization:
materialize_egeria_summary(summary, columns_struct=None)turns aRelatedMetadata*Summaryinto a clean dict that includes:- relationship properties (e.g.,
assignmentType) - related element properties (e.g.,
name,qualifiedName,guid,type,description) - recursively processed
nested_elements
- relationship properties (e.g.,
- Schema‑driven promotion: if the current spec includes attributes with
detail_spec, pyegeria builds a type→key map from those linked specs and promotes matching nested elements into those keys. Example: a role that has nestedProjectitems becomes{"projects": [ ... ]}when the column points to aProjectdetail spec. - Smart Summarization: when rendering collections in tables (
LISTor CLITABLE), pyegeria now attempts to summarize them by joining the names or display names of child elements, rather than just showing a count.
- DICT: returns full nested dict/lists for downstream processing (materialized).
- JSON: returns the raw Egeria response structure.
- LIST: shows a compact summary (names/identifiers) in the master row and adds a
[details]link if adetail_specis configured. A detail section is appended below using the linked spec. - REPORT: renders nested dicts/lists as hierarchical markdown bullet lists (read‑only view).
- FORM: shows summarized values only to keep the form updateable and concise.
In base_report_formats.py:
# Master: roles column links to a detail spec
Column(name="Roles", key="roles", detail_spec="My-User-Roles-Detail")
# Roles detail spec includes a nested projects column that links again
"My-User-Roles-Detail": FormatSet(
target_type="PersonRole",
formats=[
Format(types=["LIST", "REPORT"], attributes=[
Column(name="Name", key="name"),
Column(name="GUID", key="guid", format=True),
Column(name="Projects", key="projects", detail_spec="My-User-Projects-Detail"),
])
]
)
"My-User-Projects-Detail": FormatSet(
target_type="Project",
formats=[
Format(types=["LIST", "REPORT"], attributes=[
Column(name="Name", key="display_name"),
Column(name="Qualified Name", key="qualified_name"),
Column(name="Project Status", key="project_status"),
Column(name="GUID", key="guid", format=True),
])
]
)Result:
- LIST master shows role names with a
[details]link. - A "Roles" section appears below the table; each row’s projects appear as a nested table or report using
My-User-Projects-Detail.
The REPORT-GRAPH output type produces a recursive, wiki-style Markdown document with working intra-document links. It is ideal for exploring networks of related metadata (peers and nested/child elements) from a find_* response.
Key traits
- One section per element, with an anchor (
<a id="{guid}">) and heading# <Type> Name: <Display Name> - Shows key identifiers (Qualified Name, GUID) and scalar properties
- Lists Peers and Children as bullet lists with Markdown links that jump to their sections
- Recurses to render each referenced element exactly once (subsequent references render only as links), avoiding cycles
- Works with most specs out-of-the-box due to
ALLfallback; you typically do not need to edit the spec to try it
Examples
# Generate a linked Markdown graph for governance definitions
poetry run run_report --report "Governance Policies" --output-format REPORT-GRAPH --param search_string="*"
# Any spec with types:["ALL"] can also be rendered as a graph
poetry run run_report --report "Collections" --output-format REPORT-GRAPH --param search_string="*"Notes
- The outbox file is saved as Markdown (
.md). - The top preamble (title/description) is automatically included by the executor.
- Use optional parameters (if supported by the calling client/spec), for example
page_size,start_from.
Many Egeria elements include Mermaid graph definitions to visualize relationships. pyegeria automatically detects and renders these in Markdown reports (REPORT, REPORT-GRAPH, MERMAID).
Egeria often produces Mermaid output using newer syntax features (like @ { shape: ... }) or YAML frontmatter that some Markdown renderers (e.g., Obsidian, PyCharm) do not yet support.
By default, pyegeria applies a normalization process to these graphs to ensure they render correctly across a wide range of tools.
- Line Endings: Standardizes all line endings to
\n. - Frontmatter Removal: Strips YAML frontmatter blocks (delimited by
---) from the Mermaid code, as these often cause parsing errors in older renderers. - Label Escaping: Automatically escapes literal newlines inside double-quoted labels (changing them to
\n) to prevent parser crashes. - Shape Syntax Conversion: Converts newer node-with-shape syntax to standard, broadly compatible Mermaid syntax:
roundedorstadium→(text)diamondordecision→{text}circle→((text))hexagon→{{text}}- Unknown or document shapes →
[text](standard square box)
If you are using a modern Mermaid renderer that supports the native Egeria output, you can disable this transformation by changing the NORMALIZE_MERMAID flag in pyegeria/view/output_formatter.py to False.
When generating output for complex entities like User Profiles, pyegeria can recursively search through the relationship hierarchy to find and aggregate nested elements.
For example, a User Profile report spec might include a projects attribute. Since Egeria usually returns projects nested within roles or teams, pyegeria's profile extractor automatically scans the performsRoles hierarchy to find all elements of type Project and pulls them up to the top level of your report.
- Recursive Search: Scans multiple levels deep (configurable, default is 3).
- Automatic Deduplication: Elements are automatically deduplicated by their GUID, ensuring that a project linked via multiple roles only appears once in the aggregated list.
- Type-Aware: Uses the
target_typedefined in the attribute'sdetail_specto decide what to aggregate. Matches both primary type names and supertype names.
The recursion depth can be controlled in the client code. For the MyProfile client, you can set the aggregation_depth parameter:
client = MyProfile(..., aggregation_depth=5)
# Or update it on an existing client
client.aggregation_depth = 1 # Only look at immediate children of rolesYou can add/override report specs without changing pyegeria’s source by providing JSON files and loading them at runtime.
pyegeria/view/base_report_formats.py supports loading JSON files from a user directory via:
- Environment variable
PYEGERIA_USER_REPORT_SPECS_DIR(preferred) - Fallback env var
PYEGERIA_USER_FORMAT_SETS_DIR
All *.json files in that directory are loaded and merged. Later files overwrite earlier keys on collision.
Load them early in your program:
from pyegeria.view.base_report_formats import load_user_report_specs
load_user_report_specs()Tip: You can also export the built‑in specs, edit them, and re‑load from your directory:
from pyegeria.view.base_report_formats import save_report_specs
save_report_specs("/tmp/builtin_specs.json") # dump all built‑ins
save_report_specs("/tmp/my_subset.json", ["My-User-MD"]) # dump a subsetUser JSON files contain a dictionary of FormatSets keyed by label. The shape mirrors the Pydantic models (attributes may appear as attributes or legacy columns). Example:
{
"My-Custom-Assets": {
"target_type": "Asset",
"heading": "My Asset Overview",
"description": "Compact asset table for my team",
"family": "AssetCatalog",
"formats": [
{
"types": ["LIST"],
"attributes": [
{"name": "Display Name", "key": "display_name"},
{"name": "Type", "key": "type_name"},
{"name": "GUID", "key": "guid", "format": true}
]
}
]
}
}Place this JSON file into your user specs directory and call load_user_report_specs().
- Column
keycan be snake_case or camelCase. The formatter tries exact, thento_camel_case, then uppercase (useful forGUID). - Display labels are prettified (camel/snake → Title Case) and respect common acronyms (GUID, URL, ID, API, UI).
- Use
detail_specto implement master–detail links. The linked spec’starget_typedrives automatic promotion of nested elements when possible.
- List all spec names (optionally grouped):
report_spec_list(show_family=True, sort_by_family=True) - Get a spec by name and match a type:
select_report_format("Collections", "TABLE") - Render a documentation page of all specs (names, types, columns):
report_spec_markdown() - Search by perspective or question (if a spec defines
question_spec):find_report_specs_by_perspective("Solution Architect")find_report_specs_by_question("list my teams")
pyegeria provides ready-to-use CLI commands to discover and run report specs.
list_reports: Lists all registered report specs with their Family, Description, and Available Formats. Use--search/-sto filter by name, family, description, or aliases.run_report: Executes a report spec and renders output:TABLE: paged, interactive Rich table in the terminalMD/REPORT/FORM/LIST/HTML: writes a timestamped file to your outboxDICT: returns machine-readable data (materialized)JSON: returns raw machine-readable data from Egeria
Examples
# List all reports (paged table)
poetry run list_reports
# Filter by keyword (matches name, family, description, aliases)
poetry run list_reports --search glossary
# Run a report as a table (Rich table in terminal)
poetry run run_report --report "Digital-Products" --output-format TABLE --param search_string="*"
# Run a report and save Markdown to the outbox
poetry run run_report --report "My-User-MD" --output-format REPORT --param search_string="*"
# Pass multiple parameters in snake_case
poetry run run_report \
--report "Collections" \
--output-format TABLE \
--param search_string="*" \
--param page_size=100 \
--param start_from=0
# Or pass parameters as JSON (snake_case keys)
poetry run run_report --report "Collections" --output-format TABLE \
--params-json '{"search_string":"*","page_size":100,"start_from":0}'From the main CLI (hey_egeria):
# Inside the Hey Egeria CLI
hey_egeria cat show info list-reports --search user
hey_egeria cat show info "Run Report" --report "Digital-Products" --output-format TABLE --search-string "*"Notes
- Unknown report vs unsupported format: error messages clearly distinguish a mistyped/unknown report from a known report that does not support the requested
output_format. Hints include the list of available formats and suggest runninglist_reports. - CLI parameters must use snake_case (see “Parameter naming: snake_case vs camelCase”).
If you use an MCP-compatible client, pyegeria exposes report-related tools via a lightweight server at pyegeria/core/mcp_server.py.
Start the server
# Using Poetry
poetry run pyegeria-mcp
# If installed via pip, the same entry point is available as a script
pyegeria-mcpExposed tools and parameters
list_reports(output_type="DICT"|"JSON"|"MARKDOWN")- Lists eligible report specs (those that support
DICTorALL), including description, target type, and required/optional params.
- Lists eligible report specs (those that support
describe_report(name, output_type="DICT"|"JSON"|"MARKDOWN")- Returns the schema and details for a single report spec.
MARKDOWNmaps to a narrative description;JSONmaps to DICT.
- Returns the schema and details for a single report spec.
find_report_specs(perspective=None, question=None, report_spec=None, output_type="DICT"|"JSON"|"MARKDOWN")- Searches report specs by perspective and/or example question (when provided in the spec’s
question_spec). Use"*"to skip a filter.
- Searches report specs by perspective and/or example question (when provided in the spec’s
run_report(report_name, search_string="*", page_size=0, start_from=0, starts_with=None, ends_with=None, ignore_case=None, output_type="DICT"|"JSON"|"MARKDOWN")- Executes a report spec. Prefer
output_type="DICT"for tool consumption;MARKDOWNreturns a human-readable narrative.
- Executes a report spec. Prefer
Conventions
- Parameters are snake_case, matching the rest of pyegeria. The server adapts these to on‑wire camelCase when calling Egeria.
- Return payloads follow the normalized shapes used across pyegeria’s report executor:
{"kind":"empty"}when no rows are found{"kind":"json","data": ...}for DICT/JSON results{"kind":"text","mime":"text/markdown"|"text/html","content": ...}for narrative outputs
Tip: You can combine find_report_specs to discover candidates and then run_report with the same report name.
- Cell shows
---in LIST: the value is empty or the key didn’t match. Check your columnkey(snake vs camel), and confirm your extractor/materializer emits that key. - Detail link not shown: the master column has
detail_specbut the value is empty. Ensure nested elements are materialized and (for generic promotion) that the linked spec’starget_typematches the nested element’stype. - GUID not visible: include a
Column(name="GUID", key="guid", format=True)in the detail spec. The formatter mapsguidand formats it safely for tables/links. - FORM output shows summaries only: this is by design to keep forms concise and editable.
- Specs registry and helpers:
pyegeria/view/base_report_formats.py - Output engine and materializer:
pyegeria/view/output_formatter.py - Pydantic models for specs:
pyegeria/view/_output_format_models.py - Example built‑ins (including My-User‑MD):
pyegeria/view/base_report_formats.py