Documentation

Junkt in about five minutes

Junkt is a hosted key/value store your agents reach over the Model Context Protocol. There is not much to it, which is the point — this page is most of what there is to know.

The model

Each account has exactly one flat keyspace. No buckets, no nesting, no databases to pick between. A key is a string, a value is a string, and that is the whole data model.

Keys are namespaced by convention rather than by the system. Colons are the usual separator and list_keys can filter by prefix, so a naming scheme like cowork:daily-digest:last_run gives you grouping without Junkt having to implement it.

cowork:daily-digest:last_run
cowork:daily-digest:runs
cowork:inbox:cursor
deploys:staging:cooldown

Every key belongs to exactly one account and is scoped by user at the query layer. Nobody else can list, read or write your keys.

One prefix is not your agent’s to use: control: is reserved for controls, and writes to it are refused.

Two kinds of key

A key holds either a value or a counter, and the two cannot be confused for each other:

  • Values are written with set_value and hold any string up to 2KB.
  • Counters are created and changed with increment and hold a whole number.

The type is locked in both directions. set_value refuses to overwrite a counter, and a key holding a plain value cannot be incremented. Both cases raise a clear error and leave the original key untouched, so two agents using the same key for different purposes fail loudly instead of quietly corrupting each other. The type is reported by get_value and list_keys.

Expiry

set_value takes an optional ttl in seconds. When it elapses the key stops being visible to get_value and list_keys, and stops counting toward your key limit, before it is physically removed by an hourly prune. Re-setting a key rewrites its TTL; setting it without one clears any previous expiry.

This is what makes cooldowns and rate-limit windows a one-liner. A key with a one-hour TTL is a lock that releases itself:

set_value("deploys:staging:cooldown", "held", ttl: 3600)

Controls

Everything above flows one way: your agent writes a key, and reads it back on a later run. Controls go the other way. A control is a setting you write in the dashboard, which your agent may read and cannot change — the way to tell a running agent something without editing its prompt or its schedule.

The obvious one is a stop switch:

control:paused   boolean   true
                 "If true, stop and do nothing this run."

Your agent reads its controls with list_controls. set_value, increment and delete_key all refuse the control: namespace, so it cannot clear a control you have set, including one that stops it. An agent that could switch off its own pause flag would not be paused.

Controls are the one thing in Junkt that is typed, because they are the one thing written by a person and read by a machine — the two never meet, so the type is what holds them to the same meaning:

  • boolean — a flag. On or off, and nothing in between to misread.
  • string — free text, for a short standing instruction.
  • number — a budget, a threshold, a batch size.
  • enum — one of a fixed set you define, so the agent picks from a closed list rather than interpreting prose.

Each control also carries a description, and that is the part that matters. A flag named paused means nothing to an agent on its own; the description is where you say what to do about it. It is returned with the value, and it is read as an instruction rather than as data.

Controls have their own allowance — ten on the free tier — separate from your keys, so setting one never costs you somewhere to remember something.

One thing to be clear about: Junkt cannot stop an agent by force. A control is somewhere your agent will reliably look, which only works if you have told it to look. That is worth having, and it is not the same as a kill switch. Tell your agent to check list_controls before it starts, and the mechanism works; leave that out and a paused flag sits there being ignored.

What a model cannot do for itself

An agent needs two things it has no way to produce: the current time, and a genuinely random value. Both come from tools rather than keys. Nothing is stored, there is nothing to read back, and neither costs you a key.

The time tool answers anything that turns on the real clock: what time it is now in a given timezone, how long since a stored timestamp, whether that timestamp was today, and how many seconds until midnight. That last one is a ttl for a key that clears itself at the end of the day. It matters more than it sounds — a model asked whether something ran today will otherwise guess at the date, and it guesses badly.

random_number, random_token and random_choice cover integers and probability gates, UUIDs and lock tokens, and picking or shuffling a list. Ask a model for a random number and you get the same handful of favourites; ask it to choose from a list and it leans on whichever option reads best. Neither is fit for sampling or for splitting traffic.

Pass a seed when you want the opposite of randomness — the same input returning the same answer every time, which is how you bucket an account or assign it consistently between runs.

Limits

Limit Free tier What happens at the edge
Keys per account 25 A write creating a 26th key is rejected. Expired keys do not count.
Value size 2048 bytes Rejected with the size and the limit, so the agent can trim.
Key length 128 characters Rejected. Any characters are allowed within that length.
Requests 60 / minute Per account, across all connectors.
Activity retention 30 days Older entries are pruned automatically.

The activity log

Every successful write, increment and delete is recorded with the key, the time, and whether it came from an MCP client or the web dashboard. Rejected operations leave no trace, and reads are deliberately not logged — logging every get_value would drown the useful entries.

Recent activity appears on your dashboard. It exists because handing an autonomous agent persistent memory is a lot easier when you can check what it did with it.

Security

  • Junkt is its own OAuth 2.1 authorization and resource server, with PKCE and Dynamic Client Registration, so clients register themselves with no client IDs to copy around.
  • Every token maps to exactly one account, and every query is scoped by user.
  • Tokens are listed on your dashboard with the date you approved them, and revoking one takes effect on its next call.
  • Accounts support passkeys and two-factor authentication.
  • Stored values are never written to application logs.

Next

Connect a client if you have an account, or create one first. If you want to see the exact shape of every request and response, go straight to the tool reference.

Ask an AI

Junkt is built for agents, so ask one

Pick a question and where to ask it. Each link opens a fresh chat with the question already written, including a nudge to give you a straight answer rather than a sales pitch.