Files
claude-plugins-official/plugins/mcp-server-dev/skills/build-mcp-server/references/server-capabilities.md
Den Delimarsky 48a018f27a fix(plugin): mcp-server-dev — correct APIs against spec, add missing primitives
Corrects fabricated/deprecated APIs: ext-apps App class model (not embedded
resources), real MCPB v0.4 manifest (no permissions block exists), registerTool
(not server.tool), @anthropic-ai/mcpb package name, CIMD preferred over DCR.

Adds missing spec coverage: resources, prompts, elicitation (with capability
check + fallback), sampling, roots, tool annotations, structured output,
instructions field, progress/cancellation.
2026-03-18 22:53:38 +00:00

165 lines
4.6 KiB
Markdown

# Server capabilities — the rest of the spec
Features beyond the three core primitives. Most are optional, a few are near-free wins.
---
## `instructions` — system prompt injection
One line of config, lands directly in Claude's system prompt. Use it for tool-use hints that don't fit in individual tool descriptions.
```typescript
const server = new McpServer(
{ name: "my-server", version: "1.0.0" },
{ instructions: "Always call search_items before get_item — IDs aren't guessable." },
);
```
```python
mcp = FastMCP("my-server", instructions="Always call search_items before get_item — IDs aren't guessable.")
```
This is the highest-leverage one-liner in the spec. If Claude keeps misusing your tools, put the fix here.
---
## Sampling — delegate LLM calls to the host
If your tool logic needs LLM inference (summarize, classify, generate), don't ship your own model client. Ask the host to do it.
```typescript
// Inside a tool handler
const result = await extra.sendRequest({
method: "sampling/createMessage",
params: {
messages: [{ role: "user", content: { type: "text", text: `Summarize: ${doc}` } }],
maxTokens: 500,
},
}, CreateMessageResultSchema);
```
```python
# fastmcp
response = await ctx.sample("Summarize this document", context=doc)
```
**Requires client support** — check `clientCapabilities.sampling` first. Model preference hints are substring-matched (`"claude-3-5"` matches any Claude 3.5 variant).
---
## Roots — query workspace boundaries
Instead of hardcoding a root directory, ask the host which directories the user approved.
```typescript
const caps = server.getClientCapabilities();
if (caps?.roots) {
const { roots } = await server.server.listRoots();
// roots: [{ uri: "file:///home/user/project", name: "My Project" }]
}
```
```python
roots = await ctx.list_roots()
```
Particularly relevant for MCPB local servers — see `build-mcpb/references/local-security.md`.
---
## Logging — structured, level-aware
Better than stderr for remote servers. Client can filter by level.
```typescript
// In a tool handler
await extra.sendNotification({
method: "notifications/message",
params: { level: "info", logger: "my-tool", data: { msg: "Processing", count: 42 } },
});
```
```python
await ctx.info("Processing", count=42) # also: ctx.debug, ctx.warning, ctx.error
```
Levels follow syslog: `debug`, `info`, `notice`, `warning`, `error`, `critical`, `alert`, `emergency`. Client sets minimum via `logging/setLevel`.
---
## Progress — for long-running tools
Client sends a `progressToken` in request `_meta`. Server emits progress notifications against it.
```typescript
async (args, extra) => {
const token = extra._meta?.progressToken;
for (let i = 0; i < 100; i++) {
if (token !== undefined) {
await extra.sendNotification({
method: "notifications/progress",
params: { progressToken: token, progress: i, total: 100, message: `Step ${i}` },
});
}
await doStep(i);
}
return { content: [{ type: "text", text: "Done" }] };
}
```
```python
async def long_task(ctx: Context) -> str:
for i in range(100):
await ctx.report_progress(progress=i, total=100, message=f"Step {i}")
await do_step(i)
return "Done"
```
---
## Cancellation — honor the abort signal
Long tools should check the SDK-provided `AbortSignal`:
```typescript
async (args, extra) => {
for (const item of items) {
if (extra.signal.aborted) throw new Error("Cancelled");
await process(item);
}
}
```
fastmcp handles this via asyncio cancellation — no explicit check needed if your handler is properly async.
---
## Completion — autocomplete for prompt args
If you've registered prompts or resource templates with arguments, you can offer autocomplete:
```typescript
server.registerPrompt("query", {
argsSchema: {
table: completable(z.string(), async (partial) => tables.filter(t => t.startsWith(partial))),
},
}, ...);
```
Low priority unless your prompts have many valid values.
---
## Which capabilities need client support?
| Feature | Server declares | Client must support | Fallback if not |
|---|---|---|---|
| `instructions` | implicit | — | — (always works) |
| Logging | `logging: {}` | — | stderr |
| Progress | — | sends `progressToken` | silently skip |
| Sampling | — | `sampling: {}` | bring your own LLM |
| Elicitation | — | `elicitation: {}` | return text, ask Claude to relay |
| Roots | — | `roots: {}` | config env var |
Check client caps via `server.getClientCapabilities()` (TS) or `ctx.session.client_params.capabilities` (fastmcp) before using the bottom three.