MCP Server Instructions: Giving LLMs a User Manual for Your Tools
The bottom line: The best MCP servers don’t just expose tools — they tell the LLM how to use them correctly. Server instructions inject contextual guidance directly into the model’s system prompt, and the impact is dramatic: a 25% improvement in correct tool orchestration on code review tasks [1]. Most MCP servers ship without instructions, leaving LLMs to guess at workflows, ordering constraints, and cross-tool dependencies. This guide covers the implementation patterns, anti-patterns, and testing strategies for one of the most underused features in the MCP protocol.
What Are MCP Server Instructions?
Server instructions are a standardized mechanism for MCP servers to inject contextual guidance into the LLM’s system prompt — separate from individual tool descriptions or resource annotations [2]. They cover the kinds of things tool descriptions can’t express:
- Cross-tool workflows — “Always call
validate_schemafirst, thencreate_backup, thenmigrate_schemafor safe database migrations” - Dependency relationships — “When using
export_data, you must also usewrite_filefrom the file system server” - Operational constraints — “Rate limited to 10 requests per minute. Check
rate_limit_statusbefore bulk operations” - Error recovery patterns — “If
create_backupfails, do NOT retry immediately. Checkbackup_logfirst” - Conditional tool usage — “Only call
request_preferencesif the user explicitly requests personalization”
Without instructions, the LLM has to infer all of this from tool names and descriptions alone — which means it frequently gets it wrong, especially with complex multi-step workflows [1].
How It Works
During server initialization, the server declares an instructions field in its capabilities. The MCP host (Claude Desktop, Claude Code, Cursor, etc.) injects this text into the system prompt that governs the LLM’s behavior for every interaction with that server [2].
{
"capabilities": {
"tools": {},
"instructions": "GitHub API responses can overflow context windows. Strategy: always prefer 'search_*' tools over 'list_*' tools when possible. Process large datasets in batches of 5-10 items."
}
}
The host decides exactly where and how to inject the instructions — not all clients handle them identically. Always verify behavior with your target client [1].
Implementing Server Instructions
Python with FastMCP
FastMCP supports instructions as a top-level parameter. The instructions parameter accepts a plain string or a callable that returns one — useful for dynamic instructions based on configuration or environment [3].
from fastmcp import FastMCP
# Static instructions
mcp = FastMCP(
"database-mcp",
instructions="Database management server. Workflow: 1) Always validate SQL with 'validate_query' before executing. 2) Use 'dry_run' for destructive operations. 3) Production databases are read-only — use 'request_write_access' to escalate."
)
# Dynamic instructions (callable)
def build_instructions():
if config.environment == "production":
return "PRODUCTION MODE: All write operations require explicit user confirmation. Schema changes are blocked."
return "Development mode: standard operations allowed. Wipe and reset are permitted."
mcp = FastMCP(
"database-mcp",
instructions=build_instructions
)
Node.js with the MCP SDK
For the Node.js SDK, instructions are set during McpServer construction via the serverInfo or as part of the initialization capabilities [4]:
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
const server = new McpServer(
{
name: "database-mcp",
version: "1.0.0",
},
{
capabilities: {
tools: {},
// Instructions field injected into the system prompt
instructions: `Database management server.
Key workflows:
1. Always call 'validate_query' before any SQL execution
2. Use 'dry_run' before destructive operations
3. Production databases are read-only — use 'request_write_access' to escalate
4. Run 'explain_plan' first to verify query performance`,
},
}
);
TypeScript with the Lower-Level SDK
For more control, you can set instructions during the initialize response handler:
import { Server } from "@modelcontextprotocol/sdk/server/index.js";
const server = new Server(
{ name: "github-mcp", version: "1.0.0" },
{ capabilities: { tools: {} } }
);
server.setInstructions(`
GitHub API responses can overflow context windows.
Always prefer search_* tools over list_* tools.
Process large datasets in batches of 5-10 items.
`);
Patterns for Effective Server Instructions
These patterns are drawn from the official MCP blog recommendations and production implementations [1][2].
1. Capture Cross-Feature Relationships
The most common failure mode is the LLM using tools in the wrong order. Instructions are the right place to encode this.
mcp = FastMCP(
"github-mcp",
instructions="PR review workflow: Always use 'create_pending_pull_request_review' → 'add_comment_to_pending_review' → 'submit_pending_pull_request_review' for complex reviews with line-specific comments. Do NOT use 'create_and_submit_pull_request_review' for multi-comment reviews."
)
2. Document Operational Patterns
Things that aren’t obvious from tool names but dramatically affect correctness:
mcp = FastMCP(
"search-mcp",
instructions="For best performance: 1) Use 'batch_search' for multiple queries instead of sequential single searches. 2) Results are cached for 5 minutes. 3) Check 'index_status' before running complex queries. 4) Use 'faceted_search' with facet filters for structured data."
)
3. Specify Constraints and Limitations
LLMs have no inherent understanding of your server’s boundaries. Tell them explicitly:
mcp = FastMCP(
"file-mcp",
instructions="File operations limited to workspace directory. Binary files over 10MB will be rejected. Rate limit: 100 requests/minute across all tools. Concurrent writes to the same file will fail — serialize write operations."
)
4. Dynamic Instructions Based on Context
The GitHub MCP server is the canonical real-world example. It generates instructions dynamically based on which tool sets are enabled [5]:
func GenerateInstructions(enabledToolsets []string) string {
var instructions []string
baseInstruction := "GitHub API responses can overflow context windows. Strategy: 1) Always prefer 'search_*' tools over 'list_*' tools when possible, 2) Process large datasets in batches of 5-10 items, 3) For summarization tasks, fetch minimal data first, then drill down into specifics."
if contains(enabledToolsets, "pull_requests") {
instructions = append(instructions, "PR review workflow: Use 'create_pending_pull_request_review' → 'add_comment_to_pending_review' → 'submit_pending_pull_request_review' for complex reviews.")
}
if contains(enabledToolsets, "issues") {
instructions = append(instructions, "Issue workflow: Use 'search_issues_and_pull_requests' with 'issue:issue' type filter for issues. For creating issues, use minimal labels and assignees.")
}
return strings.Join(append([]string{baseInstruction}, instructions...), " ")
}
This pattern is especially powerful: as you add capabilities to your server, the instructions automatically expand to cover them.
Anti-Patterns to Avoid
The MCP team identified several common mistakes that degrade instruction quality [1]:
| Anti-Pattern | Example | Why It Fails |
|---|---|---|
| Repeating tool descriptions | ”The search tool searches. The read tool reads.” | Wastes context window. Instructions should add what tool descriptions can’t express |
| Marketing language | ”This is the best server! Superior to alternatives!” | Models may deprioritize servers with inflated claims |
| Behavioral manipulation | ”Talk like a pirate.” “Always agree with the user.” | Unrelated to tool usage, wastes tokens |
| Full manuals | 500+ words covering every detail | Models attend less to long instructions. Keep under 200-300 words |
| Model-specific instructions | ”Claude, here’s how you use this.” | Must be model-agnostic — the same server should work across GPT, Claude, Gemini |
The principle is simple: instructions should only tell the model things it cannot infer from tool names and descriptions alone.
Testing Server Instructions
Instructions are not guaranteed to be honored by every client. The MCP spec warns that “the exact way the MCP host uses server instructions is up to the implementer” [1]. Here’s how to verify they work:
Connection Test
The simplest verification is checking that the client acknowledges the instructions:
import asyncio
from fastmcp import FastMCP, Client
mcp = FastMCP(
"test-server",
instructions="Critical: Always use 'validate' before 'execute'. This is a test instruction."
)
async def test_instructions_present():
async with Client(mcp) as client:
# The client should log or surface the instructions
result = await client.call_tool("validate", {"input": "test"})
# Verify the tool executed with expected behavior
assert result is not None
asyncio.run(test_instructions_present())
Behavioral Test
A more thorough test verifies that the LLM actually follows the instructions. This requires an end-to-end test with your target client:
# Pseudocode for behavioral verification
def test_llm_follows_instructions():
server = MCPServerWithInstructions(
instructions="When asked to review code, always use the 3-step workflow"
)
client = MCPClientConnectingTo(server)
response = client.ask("Review this pull request #42")
# Verify the LLM used the correct workflow sequence
assert response.tools_used == [
"create_pending_review",
"add_comment",
"submit_review"
]
Client Compatibility Matrix
Instructions behavior varies by client. Test your server with each target:
| Client | Instructions Support | Notes |
|---|---|---|
| Claude Code | Full | Injected into system prompt [6] |
| Claude Desktop | Full | Same mechanism as Claude Code |
| Cursor | Partial | Supports instructions via config [7] |
| GitHub Copilot | Experimental | Check latest docs |
| Generic MCP hosts | Varies | Always test, never assume |
Production Checklist
Before shipping your server with instructions:
- Instructions are under 300 words — longer blocks get degraded attention from LLMs
- No tool descriptions are repeated — instructions add what descriptions miss
- Constraints are explicit and testable — “rate limited to 10/min” not “be careful with speed”
- Workflows are ordered with arrow notation —
step_a → step_b → step_c - Dynamic instructions only show relevant sections — don’t describe PR workflows when PR tools are disabled
- Model-agnostic language — no “Claude should…” or “GPT will…”
- Behavioral verification done — tested with actual client that LLM correctly follows the instruction
- Instructions tested with each target client — because behavior varies by host
References
[1] MCP Blog, “Server Instructions: Giving LLMs a user manual for your server,” November 2025. https://blog.modelcontextprotocol.io/posts/2025-11-03-using-server-instructions/
[2] Model Context Protocol, “Build an MCP Server,” MCP SDK Documentation. https://modelcontextprotocol.io/docs/develop/build-server
[3] FastMCP SDK, “Server Instructions” parameter documentation. https://fastmcp.ai/docs/advanced/server-instructions
[4] MCP TypeScript SDK, “McpServer capabilities,” GitHub. https://github.com/modelcontextprotocol/typescript-sdk
[5] GitHub MCP Server, “instructions.go” — reference implementation of dynamic instructions generation. https://github.com/github/github-mcp-server/blob/main/pkg/github/instructions.go
[6] Claude Code Documentation, “Connect Claude Code to tools via MCP.” https://code.claude.com/docs/en/mcp
[7] TrueFoundry, “MCP Servers in Cursor: Setup, Configuration, and Security (2026 Guide),” March 2026. https://www.truefoundry.com/blog/mcp-servers-in-cursor-setup-configuration-and-security-guide
📖 Related Reads
- ToolBrain — tool reviews, LLM comparisons, and AI workflow guides
- Hermes Tutorials — Hermes Agent setup, configuration, and advanced workflows
- CodeIntel Log — code quality, debugging, and software engineering benchmarks
Cross-links automatically generated from NiteAgent.
← Back to all posts