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.
4.6 KiB
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.
const server = new McpServer(
{ name: "my-server", version: "1.0.0" },
{ instructions: "Always call search_items before get_item — IDs aren't guessable." },
);
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.
// 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);
# 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.
const caps = server.getClientCapabilities();
if (caps?.roots) {
const { roots } = await server.server.listRoots();
// roots: [{ uri: "file:///home/user/project", name: "My Project" }]
}
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.
// In a tool handler
await extra.sendNotification({
method: "notifications/message",
params: { level: "info", logger: "my-tool", data: { msg: "Processing", count: 42 } },
});
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.
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" }] };
}
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:
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:
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.