MCP Updates#666
Merged
Merged
Conversation
Closes #661 follow-up. - Empty-result test now uses google.cloudkms.key_rings (canonical empty-table case in the mock backend), and extractQueryResults handles the nil SQLResult that PrepareResultSet emits for empty RowMap as a zero-row result instead of an extraction failure - that's why list_registry against a fresh registry was returning rc=2. - Literal-select assertion escapes &{ so Robot Framework doesn't parse it as a dictionary-variable opener. - list_registry test asserts only rc=0 and absence of extraction failure so it works against both the canonical (test/registry) and mocked-empty (test/registry-mocked) registry configurations CI exercises. - extractQueryResults: initialise rv as empty slice, guard nil row at EOF, honour GetError(); pairs with the (rv, ok) return so empty results render as "**no results**" rather than failing extraction. Co-authored-by: Jeffrey Aven <jeffreyaven@users.noreply.github.com>
general-kroll-4-life
approved these changes
May 30, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Description
Closes the discover -> pull -> query loop in the MCP server so an agent can find providers in the registry, pull them, and query without a human pre-pulling from a shell. Bundles two read-path fixes the new tools depend on.
extractQueryResultsreturned(rv, ok && len(rv) > 0), so a zero-row result was mapped tofailed to extract query results. Now returns(rv, ok)and treats a nilSQLResult(whatPrepareResultSetemits for an emptyRowMap) as a zero-row result rather than an error.rvis initialised as an empty slice so it survivesQueryResultDTO.Rowsschema validation downstream. Repro: fresh approot, no providers pulled,list_providers->failed to extract query results.RenderTable.dataRowandRenderKVformatted with%v, assuming scalars; literal columns carry a pointer-to-nullable-wrapper so cells rendered as&{ok true}. Added anunwraphelper (handles value + pointer forms ofsql.NullString,NullBool,NullInt64,NullInt32,NullFloat64,NullByte,NullTime, genericsql.Null[T]) applied before formatting in both renderers. Invalid wrappers render as empty cells. Repro:SELECT 1 as n, 'ok' as status->| &{ok true} | &{1 true} |. Provider-backed columns were unaffected (unwrapped upstream).list_registrytool. Lets an agent see providers available in the registry to pull, distinct fromlist_providers(already-pulled). Optionalproviderarg lists versions for that provider; omitted lists all. New interrogator methodGetRegistryList(provider string)buildsREGISTRY LIST [<provider>]; backendListRegistryroutes throughrunPreprocessedQueryJSON. Table renderer. Registered withselectGatesemantics (read-only, allowed in all modes).pull_providertool. Lets an agent install a provider into the approot cache so subsequent queries resolve. Requiredprovider, optionalversion. New interrogator methodGetRegistryPull(provider, version string)buildsREGISTRY PULL <provider> [<version>]; backendPullProviderroutes throughexecQuery. KV renderer ({messages, timestamp}). Allowed in all modes: pulling fetches remote content and writes local state but does not touch any cloud control/data plane, so it is classifiedQueryClassSelect, not a cloud mutation. If a human-in-the-loop is wanted later, this would become a newregistry_pullquery class gated like lifecycle.Forbidden-character guard (space,
;,\) mirrors the CLIregistrycommand on both new tools so user-supplied identifiers cannot smuggle a second statement.Why CI didn't catch the read-path fixes
No test asserted on an empty result set, and query/metadata tests assert on
structuredContentJSON + row counts, never on rendered cell values; every test SELECT hit a real provider table, so no test ran the literal/no-FROM shape that exposes the wrapper.Robot scenario notes
MCP HTTP Empty Result Renders Cleanlyasserts on the wire shape"rows":[], because the mcp_client'sformatToolResultprefersStructuredContent(the JSON DTO) over the renderer's**no results**text. The renderer's empty branch is still covered byTestRenderTable_Empty/TestRenderKV_Empty.MCP HTTP List Registry Returns Available Providerspins the any-sdk refusal'registry list' is meaningless in local mode, because the MCP servers in this suite use afile://registry URL (per_get_registry_no_verify), and any-sdk'sListAllAvailableProvidersdeliberately refuses for local-mode registries. That refusal is the contract for this config; the tool surface and round-trip are covered end-to-end by the Go unit tests against an HTTP-shaped backend.Type of change
Issues referenced.
Closes #661
Evidence
TestRenderTable_UnwrapsNullableWrappers,TestRenderKV_UnwrapsNullableWrappers,TestRender_InvalidNullableRendersAsEmpty,TestTool_ListRegistry_RendersTableAndForwardsProvider,TestTool_ListRegistry_AllowedInReadOnly,TestTool_PullProvider_RendersKVAndForwardsArgs,TestTool_PullProvider_AllowedInReadOnly, plusTestRegistration_EnabledToolsFiltersextended to assert the two new tool names are filterable.test/robot/functional/mcp.robot:MCP HTTP Empty Result Renders CleanlyMCP HTTP Literal Select Renders Unwrapped ScalarsMCP HTTP List Registry Returns Available ProvidersMCP HTTP Pull Provider Installs Known ProviderChecklist:
Variations
None.
Tech Debt
Zero technical debt accrued. The read-path fixes are additive on the
(rv, ok)contract and provider-backed output is unchanged. The new tools route through the existing interrogator + backend + gate pattern with no new abstractions. The only open design decision (gating class forpull_provider) is resolved in-line: classified asQueryClassSelectbecause it writes only local cache state, with a clear migration path to a dedicatedregistry_pullclass if a human-in-the-loop is ever wanted.