Skip to content

feat(serve): say where a filing was read from and where exports go - #87

Merged
jfrench9 merged 1 commit into
mainfrom
feature/serve-origin-and-exports
Sep 30, 2026
Merged

jfrench9 merged 1 commit into
mainfrom
feature/serve-origin-and-exports

Conversation

@jfrench9

Copy link
Copy Markdown
Member

Summary

Two things the server didn't say, both found while using it from real MCP clients.

Where a filing came from. A ticker or cik:accession loads the published TAVI (or holon) from the RoboSystems public data CDN first. That's RoboSystems' parse of the filing, not the filing as filed, and nothing in the answer said so; the load receipt carried only "source": "V 10-K".

  • A published copy lacks Arelle's fact hashes (so fact ids differ from an EDGAR parse), concept references, and the definition arcs a hypercube can't express, and it's as current as the xbrlkit that processed it.
  • --pure, documented as the faithful-reading profile, read it too.
  • The opt-out, XBRLKIT_ARTIFACTS_URL, was documented only in a code comment.

Where exports go. Without --out-dir, serve wrote to ./output relative to whatever working directory the client chose.

  • Claude Desktop starts a stdio server in /, so every export failed there, with nothing the model could act on.
  • Claude Code starts it in the open project, so exports landed inside that repo.
  • The export result returned the relative path, which didn't say where the file was.

Changes

  • serve/session.py:
    • LoadedFiling.read_from. A CDN load records {"kind": "published", "url": <the TAVI or holon actually read>, "note": …}.
    • Every other load records edgar, file (with the absolute path), url, or filings.xbrl.org.
  • serve/tools.py:
    • describe_filing and the load_filing receipt return read_from.
    • _export_dir resolves the export folder to an absolute path and creates it. If it can't, it raises a tool error telling the model to restart with --out-dir.
  • serve/server.py:
    • DEFAULT_EXPORT_DIR = ~/xbrlkit/output, used when --out-dir isn't given.
    • The folder is printed at startup for both transports and named in export_filing's description.
  • cli.py:
    • --out-dir defaults to that folder.
    • _serve_config turns the CDN off under --pure unless XBRLKIT_ARTIFACTS_URL is set.
    • The startup line says where filings load from.
  • serve/README.md: a section on the CDN (what a published copy is and lacks, read_from, how to read the filing itself), --pure's new behavior, and where exports go and why, with an --out-dir example.
  • README.md and config.py: the same, briefly.

Output Impact

CHANGED OUTPUT and CLI CONTRACT, for serve only:

  • load_filing and describe_filing gain a read_from object; nothing is removed.
  • Without --out-dir, export_filing now writes to ~/xbrlkit/output rather than ./output, and returns absolute paths.
  • serve --pure parses a ticker or cik:accession from EDGAR, not from the CDN.

xbrlkit build and fetch still default to ./output. Every projection is unchanged.

Testing

  • just test-all: 667 passed, 2 skipped; ruff check, ruff format --check and basedpyright clean.
  • New tests, all six failing against main's source:
    • test_pure_parses_from_edgar_unless_the_source_is_chosen;
    • test_an_unwritable_export_folder_says_to_set_out_dir;
    • test_a_file_load_says_where_it_was_read_from;
    • test_exports_default_to_a_folder_in_home, which starts the server in / as Claude Desktop does;
    • read_from assertions in test_a_ticker_loads_the_published_tavi_first and test_an_unreachable_tavi_falls_back_to_the_holon, the second proving it records the representation actually read.
  • test_the_newest_filing_decides_never_an_older_one_with_a_holon now asserts the EDGAR origin; its fake loaded filing gained the field.
  • End to end as Claude Desktop runs it: this branch's xbrlkit serve --transport stdio started with working directory / and no --out-dir, driven by an MCP stdio client, loading MariMed's 10-K 1522767:0001522767-26-000030.
    • Product profile: read_from is published, with the tavi.json URL read, and the lpg export landed at an absolute path under ~/xbrlkit/output.
    • --pure: read_from is edgar, and the export landed in the same folder.
    • The server printed exports: … at startup.

Where a filing came from. A ticker or cik:accession loads the published TAVI
from the RoboSystems CDN first — RoboSystems' parse of the filing, not the
filing as filed — and nothing in the answer said so. load_filing and
describe_filing now return `read_from`: `published` with the URL read, or
`edgar`, `file`, `url`, `filings.xbrl.org`. `--pure`, the faithful-reading
profile, now parses from EDGAR unless XBRLKIT_ARTIFACTS_URL is set; the
variable, empty, turns the CDN off in either profile.

Where exports go. A client that launches the server picks its working
directory: Claude Desktop starts it in `/`, where `output/` cannot be created,
and Claude Code in the open project, where exports landed in the repo. Without
--out-dir, exports now go to ~/xbrlkit/output; the folder is printed at
startup, named in export_filing's description, and every export returns an
absolute path. A folder that cannot be written is a tool error that says to
set --out-dir.
@jfrench9
jfrench9 merged commit 0567781 into main Sep 30, 2026
4 checks passed
@jfrench9
jfrench9 deleted the feature/serve-origin-and-exports branch September 30, 2026 06:48
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant