Shoddy · Picker

Tools

The first of three levels. A server, its connection, and the gate every query passes through. The picker takes what the login can reach and changes none of it.

This page describes the tools level, which is always on and which no setting turns off. All it needs is a .picker.json that names a server and says how to connect to it. With nothing more in the file, you get every database the login can reach, and every query goes through the same gate.

pick does three kinds of work, called levels. Each level is a section of .picker.json with the same name, and you add the second and third only when a project needs them.

LevelOn whenWhat it adds
toolsalwaysa connection, the five verbs, the query gate and the caps. This page
guardrailsthe file has a guardrails sectionthe databases you declare, the verbs each allows, scopes and hidden columns. Guardrails
screeningsthe file has a screenings sectiona check on every answer for regulated data, such as a social security number. Screenings
The pick tools level. A .picker.json with only a tools section names the server main and its connection, env:MAIN_SQL. An assistant or a script sends a query to pick, which passes it through the query gate: one SELECT, parsed whole, no second statement, no sys catalog, no linked server, no write verb. On the other side is server main, with the databases Sales and Finance: every database the login can reach. No databases are declared, and there are no scopes or column rules.
The tools level. The file names a server and a connection and nothing else, so every database the login can reach is there. The gate in the middle is the same at every level.

A tools-only file, and what it gives you

A tools-only file is a connection and nothing else. It names each server under tools and gives each one a connect, which says how to reach the server. Here is the smallest useful file, beside the answer pick catalogs gives for it.

.picker.json

{
  "tools": {
    "servers": {
      "main": {
        "connect": "env:MAIN_SQL"
      }
    }
  }
}

pick catalogs

levels
  tools       on, always; settings from C:\github\app\.picker.json
  guardrails  off
  screenings  off

main  (connect: env:MAIN_SQL)
  every database this login can reach; guardrails are off, so none is declared
source: C:\github\app\.picker.json

Where the connection comes from

The best place for the connection string is an environment variable. "connect": "env:MAIN_SQL" tells pick to read the string from the variable MAIN_SQL when a call needs it, so nothing secret sits in any file. Set the variable before you start pick, or before you start the assistant that runs it.

Windows · PowerShell
$env:MAIN_SQL = "Server=localhost;Integrated Security=true;TrustServerCertificate=true"
pick catalogs
macOS and Linux
export MAIN_SQL="Server=localhost;User ID=pick_reader;Password=...;TrustServerCertificate=true"
pick catalogs

Which databases are there

Every online database the login can enter is there. The login is the account pick connects as. pick asks the server for this list each time a call needs it, so a database made a minute ago shows up on the next call.

The list leaves out the four system databases: master, tempdb, model and msdb. They hold the server's own state, not anybody's data. If you need one, declare it under guardrails.

Each database on the list allows all three grant words: list, select and describe. list lets objects show a table, select lets a query read its rows, and describe lets a caller read its columns and keys.

The login decides how long the list is. A login that can enter two databases gives a list of two, which is one reason to connect as a login with only the rights pick needs. The login shows the grants.

The query gate

The query gate reads every statement before it reaches the server. It is the same at every level, because guardrails do not change it and no setting turns it off.

The gate first parses the statement. To parse is to break the text into its parts, such as the tables it names and the columns it asks for. The gate then checks each part against a list of parts it knows. If any part is not on that list, the gate refuses the whole statement.

The gate refusesBecause
a second statement in the same callone call carries one SELECT, parsed whole
a write: INSERT, UPDATE, DELETE, MERGE, EXEC, or SELECT … INTOpick has no write verb. It reads, and that is all
sys.*, INFORMATION_SCHEMA, and OBJECT_DEFINITIONthese are the server's own catalog. describe answers the same questions
a four-part name, which reaches a linked servera linked server is a second server reached through the first
anything the gate does not knowit fails closed. An unknown part is refused, never passed

What passes is rewritten before it runs. Every table name becomes its full three-part name, Database.schema.object. SELECT * becomes the list of columns, and the answer shows that list. The reference lists every refusal.

How a name is found

There is no default database. pick never asks the connection which database it is in, and looks for every name in the databases on the list instead.

$ pick select "SELECT TOP 5 * FROM Customers"
pick: 'Customers' is in more than one place: Archive.dbo.Customers, Sales.dbo.Customers. Qualify it (Customers)
$ pick select "SELECT TOP 5 * FROM Sales.dbo.Customers"

How a name resolves has the full rules.

The caps

Every answer has a size limit, and the answer says when it hit one. The limits keep one answer from filling the assistant's context, which is the text it can hold at once.

CapLimitWhen it is reached
rows from select100. --limit N changes itthe answer says it was cut and how to narrow the query
characters in one cell2,000the cell is cut, and says how long it was
characters in one answer40,000no more rows are added, and the answer says it was cut
time for one query30 secondsthe query is stopped, with exit code 10, timed-out
objects from objects500the list ends with (truncated)

The five verbs

The verbs are the same on the command line and through the MCP server, which is the part of pick an assistant talks to. Its answer to a verb is the same as the answer to pick VERB --json.

VerbWhat it answers
catalogsthe levels, then each server and what it allows. It opens no connection, so it works while a server is down
objectsthe tables and views you may see, with their column counts and grants
selectthe rows of one SELECT, after the gate approves it
describeone table or view: its columns, types, indexes and foreign keys
doctorthe levels, whether the file loads, and whether each server answers. It never prints a connection string

What describe tells you

describe reads the server's catalog and never runs the object. For each column it gives the type and whether the column may be empty (null), and then it lists the indexes and foreign keys.

$ pick describe Sales.dbo.Orders
Sales.dbo.Orders  (table)
  OrderId  int not null
  CustomerId  int not null
  PlacedOn  date not null
  index PK_Orders (primary key): OrderId
  fk FK_Orders_Customers: CustomerId -> Sales.dbo.Customers (CustomerId)
  hash: 6f1c...

DESCRIBE, in detail is in the reference.

Check it with doctor

Run pick doctor after you write the file. It opens with the three level lines, then knocks on each server, which means it opens a connection and closes it again.

$ pick doctor
pick X.Y.Z
  ok tools: on, always; settings from C:\github\app\.picker.json
  ok guardrails: off
  ok screenings: off
  ok configuration: C:\github\app\.picker.json
  ok server main: connects (guardrails off: every database the login can reach)

A level that is off is one line, and its checks do not run. The MCP server tells the assistant the same thing, because its opening note says which levels are on for that launch.

To give an assistant these verbs, register pick with it. pick has no setup command, so registering it is one entry you write by hand, as Installing It shows.

A file from an older version

The older shape, with servers or models at the top, is refused. The message holds your own entries, rewritten under the three sections, so to fix the file you paste that part of the message over it.

pick: .picker.json has "servers" at the top, which is the shape this version no longer
reads. Rewrite it with three sections, one per level. Your entries, rewritten:
{"tools":{"servers":{"main":{"connect":"env:MAIN_SQL"}}},"guardrails":{"servers":{...}}}

When every reachable database is too many, turn on guardrails. The next page shows how to declare the databases you want and hide the rest.