AI/MCP.md
Table of Contents
Model Context Protocol (MCP)
MCP is an open protocol that standardizes how applications provide context and capabilities to LLMs. Think of it as a USB-C for AI — a common interface that lets you plug tools, data sources, and prompt templates into any MCP-compatible client (Claude Desktop, Cursor, VS Code / GitHub Copilot, etc.).
MCP Primitives
An MCP server can expose three types of primitives. Clients discover them at connection time via capability negotiation.
1. Tools
Tools are actionable functions the LLM can invoke. They are the most powerful primitive because the model decides when to call them.
| Aspect | Detail |
|---|---|
| Discovery | tools/list |
| Invocation | tools/call — client sends arguments, server returns result |
| Who calls | The LLM decides, based on the conversation context |
| Typical uses | Search, read files, run commands, query APIs, write to databases |
Example — search-index server exposing a searchNotes tool:
{
"name": "searchNotes",
"description": "Search through personal markdown notes",
"inputSchema": {
"type": "object",
"properties": {
"query": { "type": "string" },
"limit": { "type": "number", "default": 10 }
},
"required": ["query"]
}
}
Configuration:
{
"mcpServers": {
"my-tools": {
"command": "node",
"args": ["/path/to/tool-server/src/index.js"]
}
}
}
2. Prompts
Prompts are reusable prompt templates that the user (or in some clients, the model) can select. They don't execute code — they return pre-written text that gets injected into the conversation.
| Aspect | Detail |
|---|---|
| Discovery | prompts/list |
| Retrieval | prompts/get — optionally with arguments |
| Who invokes | Usually the user picks from a menu; some clients allow the model to invoke |
| Typical uses | Code review templates, commit message generators, security audit checklists, architecture review prompts |
Example registration (server-side):
server.prompt("code-review", "Perform a thorough code review", () => ({
messages: [
{
role: "user",
content: { type: "text", text: "# Code Review\n\n## Overview\n\n..." }
}
]
}));
Prompt with arguments:
server.prompt(
"git-commit",
"Generate a commit message",
{ type: "object", properties: { style: { type: "string" } } },
({ style }) => ({
messages: [
{
role: "user",
content: {
type: "text",
text: `Write a ${style ?? "conventional"} commit message...`
}
}
]
})
);
3. Resources
Resources expose read-only data — files, database records, API responses — in a structured, URI-addressable way. They are similar to GET endpoints in REST.
| Aspect | Detail |
|---|---|
| Discovery | resources/list — returns URI + MIME type |
| Retrieval | resources/read — by URI |
| Who reads | Client or model, depending on capability |
| Typical uses | Documentation snippets, configuration files, reference data, static content |
| URI scheme | Custom, e.g. config:///app, notes:///docker, prompts:///code-review |
Example:
server.resource(
"config-file",
"config:///app",
{ description: "Application config", mimeType: "application/json" },
async () => ({
contents: [{ uri: "config:///app", mimeType: "application/json", text: '{"port": 3000}' }]
})
);
4. Resource Templates
A resource template is a parameterized resource — a URI pattern with placeholders, like an Express route parameter.
| Aspect | Detail |
|---|---|
| Discovery | resources/templates/list |
| Usage | Client substitutes parameters and calls resources/read with the concrete URI |
| Typical uses | notes:///{path}, users:///{id}/profile |
Example:
server.resourceTemplate(
"note-content",
"notes:///{path}",
{ description: "Read a specific note by path" },
async (uri, { path }) => {
const content = await readNote(path);
return { contents: [{ uri, mimeType: "text/markdown", text: content }] };
}
);
How Clients Use Each Primitive
The table below shows what the user experience looks like in Claude Desktop (varies by client):
| Primitive | Claude Desktop UX |
|---|---|
| Tool | Model auto-decides to call — shown as a tool-use step in the conversation |
| Prompt | Appears in a prompt menu or / slash-commands; user picks one to insert |
| Resource | Can be attached like a file; some clients show resource browsers |
| Resource Template | Dynamic — filled out by the client URI builder or by the model |
Transport Options
MCP supports two transport protocols:
| Transport | Description | Best for |
|---|---|---|
| stdio | Server is a child process. Communicates via stdin/stdout. | Local servers (Claude Desktop, Cursor, VS Code) |
| SSE (Server-Sent Events) | Server is an HTTP endpoint. Client connects via HTTP POST/SSE. | Remote servers, multi-client, web apps |
The transport determines the command/args (stdio) or url (SSE) in the client config.
Local Prompt Server Reference
mcp-prompts — /Users/dominick/Work/llm/mcp-prompts
A custom MCP server that exposes markdown prompt templates as both Prompts and Resources.
Server details:
| Field | Value |
|---|---|
| Implementation | Node.js, using @modelcontextprotocol/sdk |
| Entry point | src/index.js |
| Prompts directory | prompts/*.md |
| Transport | stdio |
| Registration | Each .md file → 1 Prompt + 1 Resource |
Available prompts:
| Name | Description |
|---|---|
accessibility-audit |
WCAG accessibility audit |
add-documentation |
Add documentation to code |
add-error-handling |
Add error handling to code |
code-review |
Thorough code review |
create-pr |
Create a pull request |
deslop |
Clean up / polish code |
diagrams |
Generate diagrams |
generate-api-docs |
API documentation generator |
generate-pr-description |
Generate a PR description |
git-commit |
Create a commit message |
optimize-performance |
Optimize code performance |
security-review |
Security-focused code review |
visualize |
Visualize code or architecture |
write-unit-tests |
Write unit tests |
How it works:
- On startup, scans
prompts/for all.mdfiles. - Each file is parsed for YAML frontmatter (
title,description) and body content. - Registered as an MCP Prompt (
prompts/list,prompts/get) and an MCP Resource (resources/list,resources/readwith URI schemeprompts:///<name>). - Also registers an empty tools capability to prevent
Method not founderrors from clients that always calltools/list.
Configuration (in Claude Desktop or VS Code):
{
"mcpServers": {
"local-prompts": {
"command": "/Users/dominick/.nvm/versions/node/v22.22.0/bin/node",
"args": ["/Users/dominick/Work/llm/mcp-prompts/src/index.js"]
}
}
}
Using a full path to Node (via nvm) ensures the correct version is used regardless of shell environment.
Adding a new prompt: Drop a new .md file into prompts/ with optional YAML frontmatter and any markdown content. No code changes needed.
Prompt file format:
---
title: My Custom Prompt
description: A brief description shown in the client picker
---
# My Custom Prompt
## Overview
What this prompt does.
## Steps
1. First step...
2. Second step...
notes-mcp — /Users/dominick/Work/notes/mcp
An MCP server that exposes the markdown notes in this repo as Tools — the model can search, read, create, update, and list notes. It lives alongside the notes web app so it shares the same Notes/ directory.
Server details:
| Field | Value |
|---|---|
| Implementation | Node.js (ESM), using @modelcontextprotocol/sdk |
| Entry point | src/index.js |
| Notes directory | ../Notes (resolved relative to the server, or set via NOTES_DIR env var) |
| Transport | stdio |
| Primitive | Tools only |
Available tools:
| Tool | Description |
|---|---|
searchNotes |
Search notes by filename, frontmatter title, or content. Returns scored results. Use before reading or editing to find the right file. |
readNote |
Read the full contents of a note by relative path (e.g. AI/MCP.md). |
createNote |
Create a new markdown note at a given path. Creates parent directories. Refuses to overwrite unless overwrite: true. |
updateNote |
Update an existing note — either full content replacement or targeted find-and-replace (old_string → new_string). |
listNotes |
List all .md files, optionally scoped to a subdirectory. Use to browse or get an overview. |
How it works:
- On startup, resolves the
Notes/directory relative to the repo root (__dirname/../../Notes). searchNoteswalks all.mdfiles, scores them against the query (exact match → starts-with → contains → fuzzy subsequence), and returns ranked results.readNote/createNote/updateNoteall resolve paths safely (no traversal outsideNotes/).updateNotesupports two modes: full content replacement (contentparam) or surgical edits (old_string+new_string).
Intent routing — how the model picks the right tool:
The tool descriptions handle intent routing naturally:
| User says | Model does |
|---|---|
| "update my docs about X" | searchNotes → readNote → updateNote |
| "create a note in my docs folder" | createNote |
| "what notes do I have?" | listNotes |
| "find my note about X" | searchNotes |
| "show me my MCP docs" | searchNotes({ query: "MCP" }) → readNote |
Configuration (in ~/.mcp.json):
{
"mcpServers": {
"notes": {
"command": "node",
"args": ["/Users/dominick/Work/notes/mcp/src/index.js"]
}
}
}
The server uses __dirname-relative path resolution for the notes directory, so it works regardless of the client's working directory. No cwd setting needed.
Alternative Configuration Approaches
The mcpServers config above uses a local absolute path, which is fragile — it breaks if the project is moved, renamed, or cloned to a different machine. Below are portable alternatives.
Option 1: Publish to npm (Cleanest)
Publish the server as an npm package, then reference it with npx:
{
"mcpServers": {
"local-prompts": {
"command": "npx",
"args": ["-y", "mcp-prompts-server"]
}
}
}
npxdownloads and runs the package without a global install.-yskips the confirmation prompt (critical for automated startup).- Works on any machine with Node.js.
Setup required:
- Add a
"bin"field topackage.json:{ "bin": { "mcp-prompts-server": "./src/index.js" } } - Add
#!/usr/bin/env nodeshebang at the top of the entry file. - Run
npm publish.
Option 2: Reference a GitHub Repo Directly
npx can install and run a package directly from a GitHub repo — no npm publish needed.
{
"mcpServers": {
"local-prompts": {
"command": "npx",
"args": ["-y", "github:dominickp/mcp-prompts"]
}
}
}
Or the shorthand format:
{
"mcpServers": {
"local-prompts": {
"command": "npx",
"args": ["-y", "dominickp/mcp-prompts"]
}
}
}
Requirements: Same as Option 1 (bin field + shebang), plus the repo pushed to GitHub.
How it works: npx resolves github:user/repo → installs from https://github.com/user/repo → runs the binary defined in the repo's package.json. No cloning or manual npm install needed.
Option 3: Smithery (MCP Registry)
Smithery is a registry and hosting platform for MCP servers. Once registered, users install with a single CLI command. Overkill for a personal prompts server, but useful for public distribution.
Which to Choose
| Option | Effort | Portability | Best for |
|---|---|---|---|
| Local path | None | Low | Development, single machine |
GitHub + npx |
Low (shebang + bin field, push) |
High | Sharing with a team, personal use across machines |
| npm publish | Medium (npm account, publish workflow) | High | Public distribution |
| Smithery | Medium | High | Public distribution with discovery |
Recommendation: GitHub + npx is the sweet spot — minimal setup, no npm account needed, works from any machine.
Configuration Summary
Standard fields in mcpServers config:
{
"mcpServers": {
"<server-name>": {
// stdio transport:
"command": "node", // executable
"args": ["/path/to/server.js"], // arguments
"env": { "NODE_ENV": "production" }, // optional environment vars
// — OR — SSE transport:
"url": "https://example.com/mcp/sse" // SSE endpoint URL
}
}
}
Where to configure
| Client | Config file location |
|---|---|
| Claude Code (CLI) | ~/.mcp.json (user-level). Same format as Cursor/VS Code — { "mcpServers": { ... } }. Restart Claude Code after changes. |
| Claude Desktop | ~/Library/Application Support/Claude/claude_desktop_config.json (macOS) |
| Cursor | Settings → MCP Servers (or ~/.cursor/mcp.json) |
| VS Code / Copilot | ~/.vscode-server/data/Machine/mcp.json or workspace .vscode/mcp.json |
| Copilot Chat (JetBrains) | Settings → Tools → GitHub Copilot → MCP Servers |
| avante.nvim | Via mcphub.nvim: ~/.config/mcphub/servers.json. Also wire system_prompt + custom_tools callbacks in avante config. |
Reloading MCP Servers in Claude Code
When you edit an MCP server config or the server code itself, restart Claude Code and run /mcp, arrow down to the server, hit Enter, and select Reconnect.
Capability Negotiation
When a client connects to an MCP server, it sends its capabilities. The server responds with its own. Each primitive is gated behind a capability:
// Server-side capability declaration (SDK handles this for you)
server.server.registerCapabilities({
prompts: {}, // server supports prompts
resources: {}, // server supports resources + resource templates
tools: {}, // server supports tools
logging: {}, // server supports logging
});
Clients that don't support a primitive simply won't call its methods. Servers should still register empty capabilities (e.g., tools: {} with an empty handler) to prevent Method not found errors from clients that always probe.