# JSON Schema

JSON Schema is a JSON document that describes what another JSON document is allowed to look like — which fields exist, of what type, within what bounds, and which are required. It is how a tool tells a model what arguments to produce and how an API description states its payloads. It checks structure only: a perfectly valid argument can still be wrong, forbidden, or about a record that does not exist.

- **Status:** Reference
- **Audience:** developer
- **Last verified:** 2026-09-10
- **Canonical:** https://connectbyjbrh.com/docs/technology/json-schema/

## What a schema pins down

```json
{
  "type": "object",
  "required": ["person_id", "due"],
  "additionalProperties": false,
  "properties": {
    "person_id": {"type": "string", "minLength": 1},
    "channel":   {"enum": ["email", "whatsapp", "sms", "phone", "any", "task"]},
    "due":       {"type": "string", "format": "date-time"},
    "reason":    {"type": "string", "maxLength": 500}
  }
}
```

Three of those lines do most of the work. `required` turns a missing field from a runtime surprise into a rejection at the boundary. `additionalProperties: false` means a misspelt key is an error rather than a silently ignored instruction — the difference between a follow-up that does not have the reason you gave it and one that refuses to be created. And an `enum` is the only reliable way to stop a plausible-sounding invention: `"whatsapp"` is a channel, `"messenger"` is not, and a model asked for a channel will occasionally offer the second.

For a model, the schema is not validation at all — it is the instruction. It is what the model reads to decide what to send, so a `description` beside a field is a first-class piece of the design and a vague one produces vague arguments.

## What it cannot say

- **That the record exists.** `person_id` matching a string pattern says nothing about a person. A tool still has to look, and still has to answer honestly when there is nothing there.
- **That the caller may do it.** A schema has no notion of who is calling. The Assistant's rights are deliberately narrower than a person's — it cannot set pricing and cannot clear a do-not-contact entry — and none of that is expressible as a shape.
- **That the value is sensible.** A due date in 1904 satisfies `format: date-time`. So does one four years out on a follow-up nobody will ever keep.
- **That two fields agree.** Cross-field logic — this is required only when that is set — is expressible awkwardly at best. Put it in the service, where the error message can explain itself.

## Does Connect use JSON Schema?

**Used, in two places, both machine-facing.** Every MCP tool publishes an input schema, which is how any client — Claude, ChatGPT, or something you wrote — knows what to send without being told out of band. And the public OpenAPI **3.1.0** description states its request and response payloads in the JSON Schema dialect that document declares, which is what lets off-the-shelf tooling generate a client from it.

It is not used as the product's authorisation layer, and that boundary is worth stating plainly, because it is the mistake that turns a tool-using agent into an incident.

## Valid, allowed, and true are three different checks

| Check | Question | Where it lives |
|---|---|---|
| Validation | Is this the right shape? | The schema, at the edge |
| Authorisation | May this caller do this, to this record, now? | The domain service and the autonomy rules |
| Verification | Is what I was told actually so? | A read against the database |

The third row is not theoretical here. The screen context handed to the Assistant is checked against the database before it is trusted, because a well-formed statement about what is on somebody's screen is still only a claim. The same reasoning is why an agent's tools call the services that own the records rather than the tables underneath them: the service is where the rule lives, and a schema at the edge cannot enforce a rule it has never heard of.

## Questions

### Does a strict schema stop a model hallucinating?

It stops one kind: an invented field name or an invented enum value is rejected outright. It does nothing about an invented *value* in a valid field — a confident, well-typed identifier for a person who does not exist. That is caught by looking the record up, not by checking its shape.

### Should every optional field really be optional?

Optional fields are where ambiguity accumulates. A tool with fourteen optional arguments has a large space of arguments that are individually valid and collectively meaningless. Fewer fields, more `enum`s and more required ones produce better arguments from a model and clearer errors for a person.

### Can I get the schemas without calling the API?

Yes. The MCP tool list carries its own input schemas, and the public OpenAPI 3.1.0 document is published as a static file. Both are meant to be read by a machine before it makes its first real call.

## Related

- [JSON](https://connectbyjbrh.com/docs/technology/json/)
- [Tool calling](https://connectbyjbrh.com/docs/technology/tool-calling/)
- [Public MCP tools](https://connectbyjbrh.com/developers/mcp-public-tools/)
- [The OpenAPI description](https://connectbyjbrh.com/developers/openapi-description/)
- [Agent security](https://connectbyjbrh.com/docs/technology/agent-security/)
- [Why an agent's tools should call domain services](https://connectbyjbrh.com/research/agent-tools-call-services/)

## What this page is based on

- Connect capability registry (docs-source/facts.py) — PROTOCOLS.openapi, `mcp_server`, `assistant_tools`
- Connect source pack (docs-source/sources/GENERAL.md §8) — the Assistant's narrower rights; screen context is checked
- https://spec.openapis.org/ — OpenAPI 3.1.0
