Skip to main content
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 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 is generated from that same catalogue.

Make the first read

profile.get is the smallest honest first call. It takes no input:
profile.get input
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 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.
contacts.list input
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:
contacts.notes.get input
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:
contacts.notes.get result
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:
contacts.notes.get result
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.
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:
Protocol error envelope
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:
contacts.get input
A lookup that found no record of yours
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 is the full rule, with the eight record kinds and the one further text a withheld transcript carries. Handle errors and retries covers the failures that do carry a code.

Next steps

Update a preference safely

The first write, with its replay and its conflict.

Limits and budgets

The call limits and the result size budgets these reads sit under.