# The MCP server author's checklist

> Source: <https://pournasserian.com/writing/mcp-2026-server-checklist>
> Published: 2026-10-24 00:00:00+00:00

# The MCP server author’s checklist

Thirty-four parts is a lot to hold in your head while you build. So this last part folds the series into one page: a checklist to keep beside a pull request, each item linked to the part that explains it, the seven ideas I’d keep, and what I’m watching next.

This is part 35 of my series on what a Model Context Protocol (MCP) server does under the 2026-07-28 specification, and the last, after [part 34’s reference architecture](https://pournasserian.com/writing/mcp-2026-reference-architecture). The facts are as I read them in October 2026.

## In brief

1. **An MCP server in 2026 is a small, well-defined platform, not a list of tools:** five layers, from its protocol duties to its extensions.
2. **A MUST, SHOULD, MUST NOT or SHOULD NOT in the checklist is the specification’s word;** the other checks are practice I recommend, across eight areas from the protocol core to compatibility.
3. **Deprecations run on more than one clock.** Roots, sampling and logging can go no earlier than the first revision on or after 2027-07-28, the HTTP with Server-Sent Events (HTTP+SSE) transport sooner, and Dynamic Client Registration gives way to Client ID Metadata Documents.
4. **Extensions sit on top of a core that works everywhere,** and your telemetry shows which of them your users’ hosts support.

## The series on one page

An MCP server in 2026 describes itself through [`server/discover`](https://modelcontextprotocol.io/specification/2026-07-28/server/discover) and handles every request on its own terms, reading the protocol version, capabilities and identity from `_meta`, and trace context when the client sends it. It tells clients how long they can cache what it says, asks for missing input with Multi Round-Trip Requests (MRTR) without holding a connection open, reports progress, and stops when told to.

When it uses [authorization](https://modelcontextprotocol.io/specification/2026-07-28/basic/authorization) over HTTP, it validates every token as an OAuth resource server and never forwards one. It offers tools to the model, resources to the application and prompts to the user, and where both sides agree it goes further: it renders interfaces, runs long jobs and ships whole workflows as verified skills.

Each band covers a run of parts, top to bottom: [parts 3 to 7](https://pournasserian.com/writing/mcp-2026-server-discover), [parts 8 to 12](https://pournasserian.com/writing/mcp-2026-tool-definitions), [parts 13 to 17](https://pournasserian.com/writing/mcp-2026-multi-round-trip-requests), [parts 18 to 22](https://pournasserian.com/writing/mcp-2026-oauth-resource-server) and [parts 23 to 29](https://pournasserian.com/writing/mcp-2026-extensions). [Part 1](https://pournasserian.com/writing/mcp-2026-what-a-server-does) and [part 2](https://pournasserian.com/writing/mcp-2026-what-changed) set the scene, and [parts 30 to 34](https://pournasserian.com/writing/mcp-2026-end-to-end) put it together.

## The checklist

Here is my guide’s checklist, condensed. The marks in capitals are the specification’s words. The unmarked checks are practice I recommend, though some are the specification’s rules too; the part in the last column has the detail.

### Protocol core

| Check | Part | 
|---|---|
| Implement `server/discover` with`supportedVersions` ,`capabilities` ,`ttlMs` and`cacheScope` (MUST), and the optional`instructions` | [Part 3](https://pournasserian.com/writing/mcp-2026-server-discover) | 
| Read `protocolVersion` and`clientCapabilities` from`_meta` on every request, and never cache them per connection (MUST) | [Part 5](https://pournasserian.com/writing/mcp-2026-stateless-requests) | 
| Return [`-32021`](https://modelcontextprotocol.io/specification/2026-07-28/basic/index) (MUST, with status 400 on HTTP) when the operation needs a capability the request didn’t declare, and`-32022` for a protocol version you can’t serve | [Part 3](https://pournasserian.com/writing/mcp-2026-server-discover) ,[part 5](https://pournasserian.com/writing/mcp-2026-stateless-requests) | 
| Identify the server with `serverInfo` in each result’s`_meta` (SHOULD), and log`clientInfo` | [Part 3](https://pournasserian.com/writing/mcp-2026-server-discover) | 
| Set `resultType` on every result (MUST) | [Part 5](https://pournasserian.com/writing/mcp-2026-stateless-requests) | 
| Add `ttlMs` and`cacheScope` to[complete results](https://modelcontextprotocol.io/specification/2026-07-28/server/utilities/caching) of`server/discover` , the four list methods and`resources/read` (MUST), and none to an`input_required` result; use`private` when a list varies by caller | [Part 6](https://pournasserian.com/writing/mcp-2026-caching-and-pagination) | 
| Return lists in a deterministic order (SHOULD, for tools), and paginate with opaque cursors | [Part 6](https://pournasserian.com/writing/mcp-2026-caching-and-pagination) | 
| Write server instructions: purpose, boundaries, how the tools fit together and constraints, in under about 2,000 characters | [Part 4](https://pournasserian.com/writing/mcp-2026-server-instructions) | 

### Transport

| Check | Part | 
|---|---|
| On standard input and output (stdio): only protocol messages on stdout, logs on stderr, credentials from the environment | [Part 7](https://pournasserian.com/writing/mcp-2026-transports) | 
| On HTTP: require and verify [`MCP-Protocol-Version` and `Mcp-Method`](https://modelcontextprotocol.io/specification/2026-07-28/basic/transports/streamable-http) on every request, and`Mcp-Name` on`tools/call` ,`prompts/get` and`resources/read` ; return`-32020` on a mismatch | [Part 7](https://pournasserian.com/writing/mcp-2026-transports) | 
| On HTTP: no `Mcp-Session-Id` , no GET endpoint, no resumability assumptions | [Part 7](https://pournasserian.com/writing/mcp-2026-transports) ,[part 2](https://pournasserian.com/writing/mcp-2026-what-changed) | 
| Use `x-mcp-header` only on non-sensitive primitive parameters (SHOULD NOT on sensitive ones) | [Part 7](https://pournasserian.com/writing/mcp-2026-transports) | 
| A local HTTP server binds to `localhost` and validates`Origin` | [Part 7](https://pournasserian.com/writing/mcp-2026-transports) | 

### Features

| Check | Part | 
|---|---|
| Tools: valid names (1 to 128 characters from `A-Z a-z 0-9 _ - .` ), an`inputSchema` that is never null, honest annotations | [Part 8](https://pournasserian.com/writing/mcp-2026-tool-definitions) | 
| Tools: `outputSchema` and`structuredContent` where useful, plus readable`content` (SHOULD) | [Part 8](https://pournasserian.com/writing/mcp-2026-tool-definitions) | 
| Tools: `isError: true` with an actionable message for execution failures, JSON-RPC 2.0 errors only for protocol failures | [Part 9](https://pournasserian.com/writing/mcp-2026-tool-errors-and-design) | 
| Tools: output sizes within host limits, using summaries and `resource_link` s | [Part 9](https://pournasserian.com/writing/mcp-2026-tool-errors-and-design) | 
| Resources: `-32602` for not found (MUST), never an empty`contents` for a missing resource (MUST NOT), path sanitization (MUST) | [Part 10](https://pournasserian.com/writing/mcp-2026-resources) | 
| Prompts: validated arguments (MUST), free text between clear markers, arguments designed as identifiers | [Part 11](https://pournasserian.com/writing/mcp-2026-prompts) | 
| Completion for prompt and template arguments whose values can be listed | [Part 11](https://pournasserian.com/writing/mcp-2026-prompts) | 
| Lists don’t vary per connection (MUST NOT), but may vary by authorization | [Part 8](https://pournasserian.com/writing/mcp-2026-tool-definitions) | 

### Interaction patterns

| Check | Part | 
|---|---|
| MRTR for all server-to-client input (MUST), and only on `tools/call` ,`resources/read` and`prompts/get` | [Part 13](https://pournasserian.com/writing/mcp-2026-multi-round-trip-requests) | 
| Protect the integrity of `requestState` (MUST, when it influences logic), and bind the principal, an expiry and a digest of the request inside the seal | [Part 14](https://pournasserian.com/writing/mcp-2026-request-state) | 
| Request only the input types the client declared (MUST NOT otherwise) | [Part 13](https://pournasserian.com/writing/mcp-2026-multi-round-trip-requests) ,[part 15](https://pournasserian.com/writing/mcp-2026-elicitation) | 
| No side effects before the final input round | [Part 14](https://pournasserian.com/writing/mcp-2026-request-state) | 
| A non-interactive path for every elicitation, for when no user can answer | [Part 15](https://pournasserian.com/writing/mcp-2026-elicitation) | 
| Progress: values that increase (MUST), nothing after completion (MUST), rate-limited (SHOULD) | [Part 16](https://pournasserian.com/writing/mcp-2026-progress-and-cancellation) | 
| Cancellation: an HTTP disconnect is a cancel (MUST), `notifications/cancelled` honoured on stdio, cancellation passed on to the calls your tool makes | [Part 16](https://pournasserian.com/writing/mcp-2026-progress-and-cancellation) | 
| `subscriptions/listen` : acknowledgement first (MUST), only acknowledged types (MUST NOT otherwise),`subscriptionId` on every message (MUST), a graceful close (SHOULD) | [Part 17](https://pournasserian.com/writing/mcp-2026-subscriptions-listen) | 

### Security

| Check | Part | 
|---|---|
| Validate inputs, enforce access control, rate-limit and sanitize outputs (MUST) | [Part 9](https://pournasserian.com/writing/mcp-2026-tool-errors-and-design) ,[part 21](https://pournasserian.com/writing/mcp-2026-threat-model) | 
| An HTTP server that uses authorization: Protected Resource Metadata (MUST), and 401 and 403 challenges that name the `scope` (SHOULD) | [Part 18](https://pournasserian.com/writing/mcp-2026-oauth-resource-server) ,[part 19](https://pournasserian.com/writing/mcp-2026-oauth-step-up-and-entra) | 
| Validate the token’s audience (MUST), and never accept or forward a token issued for anything else (MUST NOT) | [Part 18](https://pournasserian.com/writing/mcp-2026-oauth-resource-server) ,[part 19](https://pournasserian.com/writing/mcp-2026-oauth-step-up-and-entra) | 
| On-behalf-of or token exchange for downstream calls | [Part 19](https://pournasserian.com/writing/mcp-2026-oauth-step-up-and-entra) | 
| Minimal `scopes_supported` , one complete step-up challenge (SHOULD), scope hierarchies respected (MUST) | [Part 19](https://pournasserian.com/writing/mcp-2026-oauth-step-up-and-entra) | 
| Handles and task IDs with high entropy, authorized on every call | [Part 5](https://pournasserian.com/writing/mcp-2026-stateless-requests) ,[part 26](https://pournasserian.com/writing/mcp-2026-tasks) | 
| Factual tool descriptions and instructions, with nothing that reads like an injection | [Part 4](https://pournasserian.com/writing/mcp-2026-server-instructions) ,[part 21](https://pournasserian.com/writing/mcp-2026-threat-model) | 
| Untrusted third-party content marked as data, never as instructions | [Part 21](https://pournasserian.com/writing/mcp-2026-threat-model) | 

### Operations

| Check | Part | 
|---|---|
| Continue `traceparent` ,`tracestate` and`baggage` from`_meta` when the client sends them, and propagate them downstream | [Part 20](https://pournasserian.com/writing/mcp-2026-opentelemetry) | 
| Never use `baggage` for authorization or tenancy | [Part 20](https://pournasserian.com/writing/mcp-2026-opentelemetry) | 
| Metrics: latency per tool, `isError` rate, protocol errors by code,`input_required` rate, disconnects, output size, calls by client | [Part 20](https://pournasserian.com/writing/mcp-2026-opentelemetry) | 
| Logging moved off the deprecated `notifications/message` to stderr or OpenTelemetry | [Part 20](https://pournasserian.com/writing/mcp-2026-opentelemetry) ,[part 22](https://pournasserian.com/writing/mcp-2026-migration-two-eras) | 
| Idle timeouts on intermediaries sized for long calls and listen streams | [Part 7](https://pournasserian.com/writing/mcp-2026-transports) ,[part 16](https://pournasserian.com/writing/mcp-2026-progress-and-cancellation) | 
| Idempotency keys on write tools | [Part 9](https://pournasserian.com/writing/mcp-2026-tool-errors-and-design) | 

### Extensions

| Check | Part | 
|---|---|
| Declare extensions in `server/discover` , and check each request’s declarations before using one | [Part 23](https://pournasserian.com/writing/mcp-2026-extensions) | 
| MCP Apps: `ui://` and`text/html;profile=mcp-app` , minimal Content Security Policy (CSP) domains, an explicit`prefersBorder` , app-only tools through`visibility` , and meaningful text`content` always (MUST) | [Part 24](https://pournasserian.com/writing/mcp-2026-apps) ,[part 25](https://pournasserian.com/writing/mcp-2026-apps-building) | 
| Tasks: durable creation before acknowledging (MUST), `failed` only for JSON-RPC errors (MUST NOT otherwise), unique`inputRequests` keys,`Mcp-Name` routing honoured, a durable store | [Part 26](https://pournasserian.com/writing/mcp-2026-tasks) | 
| Skills: frontmatter `name` matching the directory name (MUST), complete manifests with exact-byte digests (MUST),`skills/get` for every skill (MUST), no more than 512 files or 16 MiB per skill (SHOULD NOT exceed), no secrets | [Part 27](https://pournasserian.com/writing/mcp-2026-skills) | 
| Authorization extensions: validate the Identity Assertion JSON Web Token (JWT) Authorization Grant (ID-JAG) under enterprise-managed authorization, and prefer JWT bearer assertions for client credentials | [Part 28](https://pournasserian.com/writing/mcp-2026-enterprise-auth) | 
| Vendor `_meta` keys used only as enhancements | [Part 29](https://pournasserian.com/writing/mcp-2026-experimental-extensions) | 

### Compatibility

| Check | Part | 
|---|---|
| Serve both the stateless era and the legacy handshake era, until your telemetry says otherwise | [Part 22](https://pournasserian.com/writing/mcp-2026-migration-two-eras) | 
| Plan the removal of roots, sampling and logging: the earliest is the first revision released on or after 2027-07-28 | [Part 22](https://pournasserian.com/writing/mcp-2026-migration-two-eras) | 
| Plan it for HTTP+SSE too, which can go sooner: three months after the feature-lifecycle Specification Enhancement Proposal (SEP-2596) reaches Final | [Part 22](https://pournasserian.com/writing/mcp-2026-migration-two-eras) | 
| Move from Dynamic Client Registration to Client ID Metadata Documents | [Part 19](https://pournasserian.com/writing/mcp-2026-oauth-step-up-and-entra) ,[part 22](https://pournasserian.com/writing/mcp-2026-migration-two-eras) | 
| Test in at least three hosts, including one text-only host | [Part 25](https://pournasserian.com/writing/mcp-2026-apps-building) ,[part 31](https://pournasserian.com/writing/mcp-2026-host-support) | 
| Re-check the host support matrix every quarter | [Part 31](https://pournasserian.com/writing/mcp-2026-host-support) | 

Those dates come from the [registry of deprecated features](https://modelcontextprotocol.io/specification/2026-07-28/deprecated). Under SEP-2596, twelve months is the usual floor, but a feature that presents an active security risk can go after at least 90 days. When a window closes, the feature only becomes eligible for removal; taking it out is the core maintainers’ decision.

Hosts can ask for more than the specification, such as shorter tool names and required annotations; [my rulebook of what each AI host requires](https://pournasserian.com/writing/what-ai-hosts-require-of-an-mcp-server) lists them. For testing in both protocol eras, [my review of MCP testing tools](https://pournasserian.com/writing/mcp-server-testing-tools-2026) covers the options, from the official conformance suite to the MCP Inspector.

## Seven ideas to keep

1. **Stateless by default, stateful by design.** There are no sessions, so handles, tasks and listen streams are deliberate choices with a durable store behind them.
2. **Control determines the primitive.** The model decides on tools, the application on resources and the user on prompts ([part 12](https://pournasserian.com/writing/mcp-2026-choosing-a-primitive) ). Server instructions sit beside all three, though whether they reach the model is the host’s choice.
3. **Ask, don’t block.** MRTR turns “wait for the user” into “return and retry”. Protect`requestState` , and do nothing irreversible before the last input round.
4. **Two audiences for every result.**`content` for the model and text-only hosts,`structuredContent` for programs and apps. Under the MCP Apps convention,`structuredContent` stays out of the model’s context, so write`content` that stands on its own. Hosts differ, though:[Claude Code and its Agent software development kit (SDK)](https://code.claude.com/docs/en/agent-sdk/custom-tools) pass`structuredContent` to the model in place of the text blocks, and ChatGPT lets the model see it.
5. **Security lives in the server.** Annotations, instructions and descriptions are hints. Validation, authorization, audience checks and rate limits are what protect you.
6. **Extensions are negotiated per request and degrade gracefully.** Build the core so it works everywhere, then add MCP Apps, Tasks and Skills where hosts support them.
7. **Your telemetry is your support matrix.**`clientInfo` and the capabilities each request declares show what your users’ hosts can really do.

## What to watch next

What I’m watching, as of October 2026:

- **Tasks moving into the core.**[SEP-2663](https://modelcontextprotocol.io/seps/2663-tasks-extension) , the Tasks extension, states the intent to promote it once it stabilizes and is widely adopted ([part 26](https://pournasserian.com/writing/mcp-2026-tasks) ).
- **Skills adoption.** The specification is final, and the official SDKs are catching up: all four, for Go, TypeScript, Python and C#,[have open pull requests](https://github.com/modelcontextprotocol/ext-skills/blob/main/docs/implementations.md) , and the Go SDK’s already passes the conformance suite ([part 27](https://pournasserian.com/writing/mcp-2026-skills) ). Hosts will decide how fast servers can rely on it.
- **Interceptors.** A standard policy layer for validation, redaction and audit would change how enterprises deploy MCP. The proposal,[SEP-2624](https://github.com/modelcontextprotocol/modelcontextprotocol/pull/2624) , is still open ([part 29](https://pournasserian.com/writing/mcp-2026-experimental-extensions) ).
- **Host adoption of 2026-07-28.** The removal clock for roots, sampling and logging is running, and[part 31](https://pournasserian.com/writing/mcp-2026-host-support) shows where each host stands.
- **Enterprise identity.** Enterprise-Managed Authorization in mainstream hosts and identity providers would remove most of the consent friction in corporate rollouts ([part 28](https://pournasserian.com/writing/mcp-2026-enterprise-auth) ).

The protocol has grown from a way to give models tools into a full contract between AI applications and the systems they work with. I think the servers that do well will be the ones that treat that contract carefully: honest descriptions, strict validation, graceful degradation and good observability. Thank you for reading along. I hope the series makes your next server a little easier to build.

## Method and caveats

- Built from my guide’s checklist and conclusion, and written against the 2026-07-28 specification. Product facts are as of October 2026.
- The MUST, SHOULD, MUST NOT and SHOULD NOT marks are my guide’s, plus five that its own chapters or the specification set: `-32021` , prompt validation,`x-mcp-header` , progress rate limits and`content` in MCP Apps.
- Where the specification is more precise than my notes (the deprecation windows and Dynamic Client Registration, caching hints, `Mcp-Name` ,`-32021` , optional authorization, trace context and`instructions` ), this article follows the specification.
- Where the hosts’ docs and the extensions’ records are newer (`structuredContent` in hosts, the SDKs’ Skills work, the Interceptors proposal), it follows them, as checked on 8 October 2026.
