> ## Documentation Index
> Fetch the complete documentation index at: https://docs.coffeechats.me/llms.txt
> Use this file to discover all available pages before exploring further.

# Read your data

> Find the connector, list its tools, make a first read, page a list with a cursor, and reassemble a value that arrives in fragments.

This guide shows you how to get data out of Coffee Chats from an agent. It covers discovery, the first read, the two paging mechanisms and what to do when a read fails. It assumes the connection is already authorized: the [quickstart](https://docs.coffeechats.me/mcp/quickstart.md) covers that part.

## Find the connector

Give the agent host the connector address and nothing else. The host asks the connector which authorization server protects it, at the standard discovery address `/.well-known/oauth-protected-resource/mcp`, and the answer names that authorization server. It then reads the authorization server's own configuration document at `/oauth/mcp/.well-known/openid-configuration`. Both answers are public and neither needs a credential.

Do not configure an authorization server by hand. A host that follows the protocol finds it from those two documents.

## List the tools

Once the connection is authorized, list tools and you receive the whole catalogue. Every tool carries its risk class as protocol metadata under the key `coffeechats/risk`, with the value `read`, `safe_write`, `consequential` or `browser_handoff`, so the agent can tell a read from a write before it calls anything. The [tool reference](https://docs.coffeechats.me/mcp/tools/overview.md) is generated from that same catalogue.

## Make the first read

`profile.get` is the smallest honest first call. It takes no input:

```json profile.get input theme={"system"}
{}
```

It returns the authenticated student's own profile. If it returns that student's data, the connection is working end to end.

## Know the largest result before you ask for one

Every tool declares a maximum result size, and the connector measures the whole answer as it goes over the wire, wrapping JSON included. Most read tools are capped at 16,384 bytes. The six contact read tools are capped at 49,152, and the document reads and the apply batch list at 65,536, the largest budget in the catalogue. [Limits](https://docs.coffeechats.me/mcp/limits.md#result-size) lists every budget, and each tool's own reference page prints its number.

A result that would exceed its budget is never truncated. A tool whose single stored value can grow past the budget returns a measured fragment and a cursor instead, which the section below reassembles. Any other tool fails with the fixed text `Tool call failed.` rather than sending a partial answer, which is why the reads are narrow and paged.

## Page a list with a cursor

A list tool takes an optional `cursor` and returns `nextCursor` beside its items. Send the value back unchanged on the next call to continue, and stop when `nextCursor` is null.

```json contacts.list input theme={"system"}
{ "limit": 10, "status": "APPROVED" }
```

The cursor is opaque. The only correct thing to do with it is to send it back unchanged. The one rule worth checking is its length, which each tool's own reference page states as a bound of either 256 or 512 characters.

## Reassemble a value that arrives in fragments

Some tools read a single stored value that can be larger than the tool's result budget. Rather than truncate it, the tool returns a measured fragment. Ask for one note:

```json contacts.notes.get input theme={"system"}
{ "id": "c0ffeec0ffeec0ffee000001" }
```

The first answer carries the first chunk and a cursor. A real call cuts each chunk at the tool's byte budget, so the chunks below are short enough to fit on this page instead:

```json contacts.notes.get result theme={"system"}
{
  "fragment": {
    "id": "c0ffeec0ffeec0ffee000001",
    "jsonChunk": "\"Met at the spring fintech mixer. Follow u",
    "offset": 0,
    "complete": false
  },
  "nextCursor": "eyJpZCI6ImMwZmZlZWMwZmZlZWMwZmZlZTAwMDAwMSIsIm9mZnNldCI6NDJ9"
}
```

The `id` names the value being fragmented, `offset` says where this chunk starts in it, and `complete` says whether this is the last one. The cursor is the same identifier and the next offset, encoded. Keep calling the same tool with `nextCursor` and concatenate every `jsonChunk` in the order received. The last answer sets `complete` to true and `nextCursor` to null:

```json contacts.notes.get result theme={"system"}
{
  "fragment": {
    "id": "c0ffeec0ffeec0ffee000001",
    "jsonChunk": "p in April about the referral.\"",
    "offset": 42,
    "complete": true
  },
  "nextCursor": null
}
```

The concatenation of every `jsonChunk` is the whole document, and only then do you parse it as JSON. A note short enough to fit comes back in one answer of this second shape, with `offset` at zero.

> [!WARNING]
>
> Never parse a chunk on its own. A chunk is cut at a safe character boundary rather than at a syntactic one, so a single chunk is usually not valid JSON. The two chunks above are pieces of one JSON string: the first carries that string's opening quotation mark and the second its closing one.

A tool's own reference page says whether it can return a fragment at all.

## When a read fails

A read failure carries no code. It arrives as a protocol error holding one line of text, and that line is one of three. Almost every failure carries the fixed text:

```json Protocol error envelope theme={"system"}
{
  "isError": true,
  "content": [{ "type": "text", "text": "Tool call failed." }]
}
```

A call to a tool whose permission was not granted arrives in exactly this shape. Do not try to tell it apart from a temporary fault. Ask for the permissions you need at consent time rather than probing for them by calling tools.

The second text arrives when you look up one record by its id and the account has no such record. Ask `contacts.get` for an id the student does not have:

```json contacts.get input theme={"system"}
{ "id": "c0ffeec0ffeec0ffee000009" }
```

```text A lookup that found no record of yours theme={"system"}
No contact with that id exists for this account. Use the matching list tool to find one.
```

Eight kinds of record answer this way, and the text is the same whether the id never existed or belongs to another student. Do the thing the text asks for: call the matching list tool, `contacts.list` here, and work from the ids it returns. [Failures that carry no code](https://docs.coffeechats.me/mcp/errors.md) is the full rule, with the eight record kinds and the one further text a withheld transcript carries. [Handle errors and retries](https://docs.coffeechats.me/mcp/guides/handle-errors.md) covers the failures that do carry a code.

## Next steps

> **[Update a preference safely](https://docs.coffeechats.me/mcp/guides/update-a-preference.md)**
>
> The first write, with its replay and its conflict.

> **[Limits and budgets](https://docs.coffeechats.me/mcp/limits.md)**
>
> The call limits and the result size budgets these reads sit under.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.