feat(mcp): add custom tool profile config models - #676
Draft
vishal-bala wants to merge 1 commit into
Draft
Conversation
The declarative surface for custom tool profiles: what an author may put
in a `custom_tools:` YAML entry, and every way that entry can be wrong.
A profile is `search-records` with some arguments frozen by the author and
the rest still exposed. `lock` holds what the author decides;`params`
holds the exposure and cap policy for what the model may still pass.
Anything unlisted stays exposed, so a profile that only locks a filter
keeps the rest of the built-in's contract. A locked projection is the one
implicit case: locking it means the model cannot also choose one.
Nothing consumes these models yet -- registration and execution land in
the next commit in this stack. That ordering is deliberate: this is the
complete authoring contract and it is fully testable on its own, so it
reads before the machine that honors it. It must not reach main by itself,
though. A `custom_tools:` block would validate here and then register
nothing, with no warning -- exactly the silent-misconfiguration failure
these models otherwise guard against.
Validation is front-loaded to config load wherever the information exists
there, since the alternative is a tool that looks locked and enforces
nothing. Covered: a name that collides with a built-in, uses a reserved
`redisvl-`/`redisvl_` prefix (both separators, since the pattern permits
either), or falls outside the character set MCP hosts commonly accept; a
duplicate name; a missing `index` when several bindings exist, or one
naming an unknown binding; a `params` key that is not a real argument;
`max` on anything but `limit`; a cap above the bound index's own
`runtime.max_limit`, which could never be satisfied; hiding `query`, which
a search profile needs; and locking `return_fields` while also exposing
them. Field checks that need the inspected schema stay at startup.
All three models set `extra="forbid"`. A misspelled key would otherwise be
dropped in silence, which is the worst outcome available here: `lock:
{return_field: [...]}` would read as a locked projection while enforcing
nothing at all.
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.
Stack position: 2 of 3. Base is #675.
The declarative surface for custom tool profiles: what an author may put in a
custom_tools:YAML entry, and every way that entry can be wrong.A profile is
search-recordswith some arguments frozen by the author and the rest still exposed.lockholds what the author decides;paramsholds the exposure and cap policy for what the model may still pass. Anything unlisted stays exposed, so a profile that only locks a filter keeps the rest of the built-in's contract. A locked projection is the one implicit case: locking it means the model cannot also choose one.Read this before approving: it must not reach
mainaloneNothing consumes these models yet — registration and execution land in #676. That ordering is deliberate, because this is the complete authoring contract and is fully testable on its own, so it reads before the machine that honors it.
But this must not be merged to
mainby itself. Acustom_tools:block would validate here and then register nothing, with no warning and no error — exactly the silent-misconfiguration failure these models otherwise exist to prevent. That is the whole reason the stack targets an integration branch instead ofmain.Validation
Front-loaded to config load wherever the information exists there, since the alternative is a tool that looks locked and enforces nothing. Covered:
redisvl-/redisvl_prefix (both separators, since the name pattern permits either), or falls outside the character set MCP hosts commonly acceptindexwhen several bindings exist, or one naming an unknown bindingparamskey that is not a real argumentmaxon anything butlimitruntime.max_limit, which could never be satisfiedquery, which a search profile needsreturn_fieldswhile also exposing themField checks that need the inspected schema stay at startup, in #676.
All three models set
extra="forbid". A misspelled key would otherwise be dropped in silence, which is the worst outcome available here:lock: {return_field: [...]}would read as a locked projection while enforcing nothing at all.Verification
make check-types: clean