Skip to content

feat(oecd_query_dataset): the spilled response names the dataframe tools only in content[] #30

Description

@cyanheads

When oecd_query_dataset spills to DataCanvas it returns canvas_id, table_name, and truncated: true in structuredContent, but no field in that payload names the tools that read the staged table. The only text that names one is the format() note, which lands in content[]. A client that forwards structuredContent therefore hands the model an opaque canvas handle and no route to the rows behind it — the likely outcomes are reporting the truncation and stopping, or re-running the query with a narrower key to refetch data already staged one SQL call away.

Related: #7, #11

Proposal

Put the pointer where the token is. Emit a ctx.enrich notice on the spill path naming both dataframe tools — oecd_dataframe_describe first, since column names are needed before valid SQL can be written — and name them in the table_name output description as well. ctx.enrich merges into structuredContent and appends a trailer to content[], so a single call covers both surfaces; format() covers only one.

Where the pointer is missing today

Line numbers are src/mcp-server/tools/definitions/query-dataset.tool.ts at 0.3.0.

Layer Where Status
Runtime response spill return, lines 390–403 Missing — no ctx.enrich call on this path. The enrichment block (lines 172–187) declares only content_table_capped and content_table_rows, both set on the inline path (line 415), so nothing is emitted when a canvas handle is actually minted
Output schema — canvas_id lines 149–156 Present — names oecd_dataframe_query and oecd_dataframe_describe
Output schema — table_name lines 157–160 Missing — "Canvas table name holding the full result — present when canvas_id is set" names neither tool
Tool description line 85 Partial — "follow up with oecd_dataframe_query" omits oecd_dataframe_describe, so the describe-then-query order is not plannable from tools/list

oecd_query_dataset is the only tool that can emit a canvas handle, and only on the spill branch — spillover() registers a table exclusively when the source exceeds the preview budget, so the inline branch stages nothing. oecd_search_datasets, oecd_get_dataset_info, oecd_get_dimension_values, and oecd_list_agencies never touch the canvas. oecd_dataframe_describe and oecd_dataframe_query are the destination, not a source.

Proposed behavior

// enrichment block — add alongside content_table_capped / content_table_rows
notice: z
  .string()
  .optional()
  .describe(
    'Present when the result was staged on DataCanvas — names the staged table and the tools that read it.',
  ),

// spill path, before the early return
ctx.enrich({
  notice:
    `Staged ${result.handle.rowCount} rows to table "${result.handle.tableName}" — use ` +
    'oecd_dataframe_describe to inspect columns, then oecd_dataframe_query to analyze the ' +
    'full set with SQL.',
});

// table_name output .describe()
'Canvas table name holding the full result — present when canvas_id is set. Use as the FROM ' +
  'target in oecd_dataframe_query SQL; oecd_dataframe_describe lists its columns.',

The tool description gains oecd_dataframe_describe next to the existing oecd_dataframe_query mention, so the two-call order is visible before the tool is ever called.

notice is already the established enrichment field for this in the server — oecd_get_dimension_values declares it and calls ctx.enrich.notice(...) for its paging and no-codelist cases.

Scope

  • src/mcp-server/tools/definitions/query-dataset.tool.tsenrichment block, spill return path, table_name output .describe(), tool description

Out of scope

  • Spillover thresholds, the preview character budget, table naming, and TTLs
  • The inline path, which stages nothing and already explains itself through content_table_capped / content_table_rows
  • Adding canvas staging to tools that don't have it

Alternatives considered

Leaving the pointer in format() alone. It reaches content[] only, and the guidance needs to travel with the token on the surface where the token is machine-readable. Widening the canvas_id description further was also considered and is not enough on its own — static schema prose is read when the model plans a call, not necessarily when it reads a result.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

enhancementNew feature or request

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions