Skip to content

Update Unstructured Transform MCP tool names and add structured data extraction - #31

Open
simoncoombes wants to merge 3 commits into
NVIDIA:mainfrom
simoncoombes:rename-transform-mcp-tools
Open

Update Unstructured Transform MCP tool names and add structured data extraction#31
simoncoombes wants to merge 3 commits into
NVIDIA:mainfrom
simoncoombes:rename-transform-mcp-tools

Conversation

@simoncoombes

@simoncoombes simoncoombes commented Jul 28, 2026

Copy link
Copy Markdown
Contributor

Closes #30.

Two things, both driven by the same Unstructured Transform server release.

The rename (the part that breaks the example today)

Three MCP tools were renamed after the example landed, and the example names the old ones in its include lists, its TransformTools fields, the README, and the flow diagram. resolve_tools fails fast on a missing tool, so the example does not start.

  • transform_files is now start_transform_job
  • check_transform_status is now check_job_status
  • get_transform_results is now get_job_results

request_file_upload_url is unchanged. Verified against the live server (Unstructured Transform 0.7.2) via its get_instructions tool, not from a changelog.

Structured data extraction (new)

The same release added structured data extraction, so this adds a second function, extract_structured_data, which returns named fields as JSON instead of the whole document as text. It fits the example's existing point: the extractor consumes the Element JSON a parse produces rather than a raw file, so it is a chain of two dependent async jobs, which is a stronger case for deterministic composition than the single job the example already wraps.

agent -> extract_structured_data(source, extraction_schema, guidance)
           |-- request_file_upload_url + PUT bytes         # local files only
           |-- start_transform_job -> poll -> results      # yields output_ref
           |-- suggest_extraction_schema_for_file          # only when no schema is given
           `-- start_extraction_job -> poll -> results     # JSON returned inline

Three details in the implementation that reviewers may want to check:

  • The parse feeds the extractor by reference. A completed parse's results carry a durable output_ref next to the download_url, present whichever output format is rendered, so the function passes the reference on and never downloads the Markdown. A test asserts no GET is issued.
  • The partition strategy is chosen from the input type (vlm for PDFs, images, and PowerPoint; fast otherwise), because extraction quality is bounded by parse quality and the wrong strategy degrades silently rather than erroring.
  • Provenance is preserved. Each record wraps extracted_data with filename, filetype, processed_date_utc, and source_file_uri, and the function returns the wrapper rather than the bare data.

The agent-facing argument is extraction_schema, not schema: these parameter names become fields of a generated Pydantic model, and schema shadows a BaseModel attribute, which makes the generated model emit a warning. A test pins the field names so that does not regress.

resolve_tools is now parameterized by which tool bundle to resolve, so transform_document still requires only the four parse tools and does not gain a dependency on tools it never calls.

Verification

  • 53 unit tests pass (26 existing, 27 new); they mock the MCP tools and HTTP transfers, so no network or credentials.

  • yapf --diff clean, ruff check clean, ci/scripts/copyright.py passes, Vale passes on the README, markdown-link-check passes (9 links).

  • I ran the parse-then-extract flow by hand against the live production server first (parse an image with strategy=vlm, draft a schema from the output_ref, extract, read back provenance-wrapped JSON), so the orchestration matches real server behaviour rather than my reading of the docs.

  • A live integration test for the new function is included alongside the existing ones, marked slow/integration and skipped without UNSTRUCTURED_API_KEY. Both integration tests pass against production (pytest -k "not full_workflow" --run_integration --run_slow, about 73s for the extraction one). test_full_workflow_live is unchanged and also passes.

  • I drove the ReAct agent against the new tool by hand (nat run, the shipped config, nemotron-3-nano-30b-a3b) to check that a small model can handle a multi-argument tool input. It selects the tool and emits valid JSON for the generated schema:

    Thought: I need to extract specific fields (invoice number and total due) from the provided PDF document.
    Action: extract_structured_data
    Action Input: {"source": "/tmp/claude_agent_probe.pdf"}
    

    Omitting the optional arguments takes the intended path: the function calls suggest_extraction_schema_for_file, then start_extraction_job, and the whole chain (upload, parse, poll, suggest, extract, poll) completes in about 76 seconds.

That live test asserts the shape of the result rather than the extracted value, and the reason is worth stating. The fixture is a synthetic one-sentence PDF. The parse reads it correctly (verified: both vlm and fast return the exact sentence), but what the extractor fills in for so degenerate a document is not stable: with a magic_word field against the sentence "The magic word is xylophone." it returned "magic", and with an invoice-shaped schema on a similar fixture it returned empty strings. Asserting a value would make the test flaky for reasons that have nothing to do with this code, so it asserts the contract the function owns instead: the parse hands its output_ref to the extractor, the supplied schema shapes the output, and the provenance wrapper survives.

One incidental observation while checking that: for this synthetic PDF the server reported partitioner_type: fast_partition even when asked for vlm, which is the documented silent-fallback behaviour. The strategy choice in _partition_strategy_for still follows the server's guidance for real PDFs and images; it just is not exercised by this fixture.

Happy to split the extraction work into a follow-up PR if you would rather land the rename on its own, since that one is a straight bug fix.

Signed-off-by: Simon Coombes <simon@unstructured.io>
@simoncoombes
simoncoombes requested a review from a team as a code owner July 28, 2026 09:41
@copy-pr-bot

copy-pr-bot Bot commented Jul 28, 2026

Copy link
Copy Markdown

This pull request requires additional validation before any workflows can run on NVIDIA's runners.

Pull request vetters can view their responsibilities here.

Contributors can view more details about this message here.

Signed-off-by: Simon Coombes <simon@unstructured.io>
@simoncoombes simoncoombes changed the title Update Unstructured Transform MCP tool names Update Unstructured Transform MCP tool names and add structured data extraction Jul 28, 2026
…ve test

Signed-off-by: Simon Coombes <simon@unstructured.io>
@willkill07

Copy link
Copy Markdown
Member

/ok to test 8b1777c

@willkill07 willkill07 added bug Something isn't working non-breaking Non-breaking change labels Jul 28, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug Something isn't working non-breaking Non-breaking change

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Unstructured Transform MCP example uses tool names the server no longer exposes

2 participants