AI/MCP.md

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:

  1. On startup, scans prompts/ for all .md files.
  2. Each file is parsed for YAML frontmatter (title, description) and body content.
  3. Registered as an MCP Prompt (prompts/list, prompts/get) and an MCP Resource (resources/list, resources/read with URI scheme prompts:///<name>).
  4. Also registers an empty tools capability to prevent Method not found errors from clients that always call tools/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_stringnew_string).
listNotes List all .md files, optionally scoped to a subdirectory. Use to browse or get an overview.

How it works:

  1. On startup, resolves the Notes/ directory relative to the repo root (__dirname/../../Notes).
  2. searchNotes walks all .md files, scores them against the query (exact match → starts-with → contains → fuzzy subsequence), and returns ranked results.
  3. readNote / createNote / updateNote all resolve paths safely (no traversal outside Notes/).
  4. updateNote supports two modes: full content replacement (content param) 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" searchNotesreadNoteupdateNote
"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"]
    }
  }
}
  • npx downloads and runs the package without a global install.
  • -y skips the confirmation prompt (critical for automated startup).
  • Works on any machine with Node.js.

Setup required:

  • Add a "bin" field to package.json: { "bin": { "mcp-prompts-server": "./src/index.js" } }
  • Add #!/usr/bin/env node shebang 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.


References