# MCP tools

An MCP tool is a named function a model may call: a JSON Schema for its arguments, a description the model reads to decide whether to call it, and optional annotations hinting at its behaviour. Connect publishes ten, all read-only, all over the generated documentation corpus.

- **Status:** Reference
- **Audience:** developer
- **Last verified:** 2026-09-10
- **Canonical:** https://connectbyjbrh.com/docs/protocols/mcp-tools/

## The five fields of a descriptor

**`name`** — The stable identifier a client calls. Changing it breaks every caller, so it is chosen once.
**`title`** — A short human label for a client's own interface. Not what the model reasons over.
**`description`** — What the model reads. This is the field that decides whether it is called at the right moment, and it is therefore the field worth rewriting most often.
**`inputSchema`** — JSON Schema for the arguments: types, enumerations, minimums, defaults and the `required` list. A client validates against it before calling; a careful server validates again.
**`annotations`** — Behavioural hints — `readOnlyHint`, `destructiveHint`, `idempotentHint`, `openWorldHint` — for a client's confirmation prompts.

The description carries more weight than its length suggests. A model chooses by reading descriptions, so a vague one is never called and an overreaching one is called for work it cannot do. Connect's `search_public_docs` description ends with an explicit instruction — use this before answering any question about what Connect does — because the useful behaviour is search-before-answer, and the description is where that gets said.

```json
{
  "name": "get_product_status",
  "title": "Get capability status",
  "description": "The machine-readable capability status of Connect: what is available, what exists only as foundation, and what is not yet implemented. Check this before telling anyone Connect can do something.",
  "inputSchema": {"type": "object", "properties": {}},
  "annotations": {"readOnlyHint": true, "destructiveHint": false,
                  "idempotentHint": true, "openWorldHint": false}
}
```

## Annotations are hints, and a client is right to distrust them

Every field above is written by the server. A server that wants to be called can declare `readOnlyHint: true` on a tool that deletes records, and nothing in the protocol prevents it. The specification is explicit that annotations are hints for a client's user interface rather than a security property, and a client that skips its confirmation prompt because the server said so has delegated a safety decision to the party it is protecting itself from.

The same applies to descriptions. A tool description is text from an external system that lands directly in a model's context, which makes it an injection surface: instructions hidden in a description are read with the same attention as instructions from the user. Treat a newly added server's tool list as untrusted input and read it before enabling it.

Connect's annotations are accurate — every public one genuinely only reads generated files — but that is a fact about Connect, not a reason to trust annotations in general.

## What a tool returns

A result carries a `content` array, normally one text block. Connect also returns `structuredContent`: the same payload as JSON, so a program consuming the result does not have to parse prose that was written for a model to read. A model reads the text; a script reads the object; neither has to guess.

Failure has two shapes and they are not interchangeable. A *protocol* failure — unknown method, malformed parameters — is a JSON-RPC error object. A *tool* failure — an empty query, a page that does not exist — is a normal result with `isError: true` and a human-readable message. The second shape exists so the model can see what went wrong and try something else, which it cannot do with a transport-level error.

| Situation | Shape | Example |
|---|---|---|
| Query matched pages | `content` + `structuredContent` | Titles, URLs, summaries, status |
| Query matched nothing | Result with a `note` | "Try a broader phrase, or list_capabilities" |
| Required argument missing | `isError: true` | "`query` is required." |
| Unknown tool name | JSON-RPC `-32601` | The name is not on the published list |
| Arguments not an object | JSON-RPC `-32602` | "`arguments` must be an object" |

## Does Connect use MCP tools?

Yes, as a server. Ten are published and every one is read-only: `search_public_docs`, `fetch_public_doc`, `list_capabilities`, `get_capability`, `get_workflow`, `search_glossary`, `get_technology`, `get_protocol`, `get_product_status` and `get_changelog`. They read the generated documentation corpus — the same source the website renders — so a result cannot disagree with the page a person is reading.

There is deliberately nothing that runs SQL, nothing that proxies an arbitrary internal route, and no write that bypasses the domain service owning the record. The design rule is that a tool calls the existing service, so autonomy, approval, suppression, metering and audit still apply to anything one causes — rather than a second way into the data with its own rules.

Connect is not an MCP client. Its in-app [Connect Assistant](/docs/assistant/) has its own tools over its own services; those are a different mechanism that happens to share the word.

## Questions

### How do I know a tool will not change anything?

You cannot know it from the annotation alone, because the server wrote the annotation. What you can do is read the tool list before enabling a server, keep the client's confirmation prompts on for anything that writes, and prefer servers whose scope is narrow enough to check. For Connect's public endpoint the scope is one thing: generated documentation.

### Why do Connect's tools return both text and structured JSON?

Because two different consumers read the same result. A model reads the text block; a script reads `structuredContent` and gets typed fields without re-parsing a sentence. Returning only one of the two forces the other consumer into guesswork.

### Can I add my own tool to Connect's MCP server?

No. The list is fixed and served by Connect. If you want your agent to combine Connect's documentation tools with your own, that composition belongs in your client, which can hold several MCP servers at once.

## Related

- [Model Context Protocol](https://connectbyjbrh.com/docs/protocols/mcp/)
- [MCP resources](https://connectbyjbrh.com/docs/protocols/mcp-resources/)
- [MCP security](https://connectbyjbrh.com/docs/protocols/mcp-security/)
- [Public MCP tools](https://connectbyjbrh.com/developers/mcp-public-tools/)
- [MCP error shapes](https://connectbyjbrh.com/developers/mcp-errors/)

## What this page is based on

- https://modelcontextprotocol.io/specification/2026-07-28
- `backend/app/mcp_server.py` — PUBLIC_TOOLS and _tool_descriptors()
- `docs-source/facts.py` — CAPABILITY_STATUS['mcp_server']
