Add resources and prompts

Design useful capability boundaries

Review names, descriptions, schemas, output size, and side effects so the model can choose the right capability safely.

We have four capabilities now. Before adding more, let’s look at them the way a model does.

A model never sees our source. It sees names, descriptions, and schemas, and picks a capability from those alone. So open the Inspector, read the list, and pretend you’ve never seen server.ts. Would you know what to call?

Every capability answers one question

Here are ours side by side:

CapabilityQuestion it answersSide effect
search_notesWhich notes match this term?None
get_noteWhat does this known note contain?None
notes://catalogWhich notes are available?None
review_projectHow should a review request be phrased?None

Search and catalog overlap a little. But they support different paths: the catalog is stable context a user attaches up front, search takes a bounded query mid-task. If both returned the same complete dataset, I’d delete one.

Names, descriptions, bounds

Keep names stable. A client may have saved them. Keep descriptions concrete: “Read one project note by its exact ID” tells a model more than “Get note data”.

And bound everything: string length, result count, result size. A read-only operation can still return too much data or hammer a backend. Our search caps the query at 100 characters and results at ten. That’s part of the contract.

Separate reads from writes

If you add writes later, give each action its own name:

get_note       reads one note
update_note    changes one note
delete_note    removes one note

The lazy alternative is one manage_note tool with an action argument. Avoid it. A host can ask the user to approve delete_note before it runs. It can’t do that for manage_note, because it doesn’t know what’s about to happen. Specific names and schemas make consequential actions visible.

Annotations don’t protect anything

readOnlyHint and destructiveHint improve how a client presents a tool. Clients treat them as untrusted metadata, and nothing stops a careless server from marking a delete as read-only. Real safety comes from what the handler does, whether the caller is authorized, and what the backend credentials allow.

Remove what doesn’t earn its place

My last piece of advice: delete capabilities that don’t make the workflow clearer. Every extra tool is one more option a model can pick wrongly and one more surface to audit. A small, well-named set beats a large flexible one.

Try this on your own project: write the question-and-side-effect table for your server. If two rows answer the same question, or one side effect is unclear, that’s where to work next.

Lesson completed