|
1 | 1 | # MCP Server Style Guide |
2 | 2 |
|
3 | | -Based on the patterns observed and best practices, I recommend adopting the following consistent style guide across all your MCP servers: |
| 3 | +Based on the MCP SDK v1.22.0+ best practices and observed patterns, this guide ensures consistency across all MCP servers. |
| 4 | + |
| 5 | +## Naming Conventions |
4 | 6 |
|
5 | 7 | | Element | Convention | Rationale / Examples | |
6 | 8 | | :------------------- | :-------------------------------------------------------------------------------------------------------------------------------------------- | :------------------------------------------------------------------------------------------------------------------------------------------------ | |
7 | 9 | | **CLI Commands** | `verb-noun` in `kebab-case`. Use the shortest unambiguous verb (`ls`, `get`, `create`, `add`, `exec`, `search`). | `ls-repos`, `get-pr`, `create-comment`, `exec-command` | |
8 | 10 | | **CLI Options** | `--kebab-case`. Be specific (e.g., `--workspace-slug`, not just `--slug`). | `--project-key-or-id`, `--source-branch` | |
9 | 11 | | **MCP Tool Names** | `<namespace>_<verb>_<noun>` in `snake_case`. Use a concise 2-4 char namespace. Avoid noun repetition. | `bb_ls_repos` (Bitbucket list repos), `conf_get_page` (Confluence get page), `aws_exec_command` (AWS execute command). Avoid `ip_ip_get_details`. | |
| 12 | +| **MCP Resource Names**| `kebab-case`. Descriptive identifier for the resource type. | `ip-lookup`, `user-profile`, `config-data` | |
10 | 13 | | **MCP Arguments** | `camelCase`. Suffix identifiers consistently (e.g., `Id`, `Key`, `Slug`). Avoid abbreviations unless universal. | `workspaceSlug`, `pullRequestId`, `sourceBranch`, `pageId`. | |
11 | 14 | | **Boolean Args** | Use verb prefixes for clarity (`includeXxx`, `launchBrowser`). Avoid bare adjectives (`--https`). | `includeExtendedData: boolean`, `launchBrowser: boolean` | |
12 | 15 | | **Array Args** | Use plural names (`spaceIds`, `labels`, `statuses`). | `spaceIds: string[]`, `labels: string[]` | |
13 | 16 | | **Descriptions** | **Start with an imperative verb.** Keep the first sentence concise (≤120 chars). Add 1-2 sentences detail. Mention pre-requisites/notes last. | `List available Confluence spaces. Filters by type, status, or query. Returns formatted list including ID, key, name.` | |
14 | 17 | | **Arg Descriptions** | Start lowercase, explain purpose clearly. Mention defaults or constraints. | `numeric ID of the page to retrieve (e.g., "456789"). Required.` | |
15 | 18 | | **ID/Key Naming** | Use consistent suffixes like `Id`, `Key`, `Slug`, `KeyOrId` where appropriate. | `pageId`, `projectKeyOrId`, `workspaceSlug` | |
16 | 19 |
|
| 20 | +## SDK Best Practices (v1.22.0+) |
| 21 | + |
| 22 | +### Title vs Name |
| 23 | + |
| 24 | +All registrations (`registerTool`, `registerResource`, `registerPrompt`) support both `name` and `title`: |
| 25 | + |
| 26 | +| Field | Purpose | Example | |
| 27 | +| :---- | :------ | :------ | |
| 28 | +| `name` | Unique identifier for programmatic use | `jira_get` | |
| 29 | +| `title` | Human-readable display name for UI | `Jira GET Request` | |
| 30 | + |
| 31 | +**Always provide both** - `name` for code, `title` for user interfaces. |
| 32 | + |
| 33 | +### Modern Registration APIs |
| 34 | + |
| 35 | +Use the modern `register*` methods instead of deprecated alternatives: |
| 36 | + |
| 37 | +| Deprecated | Modern (SDK v1.22.0+) | |
| 38 | +| :--------- | :-------------------- | |
| 39 | +| `server.tool()` | `server.registerTool()` | |
| 40 | +| `server.resource()` | `server.registerResource()` | |
| 41 | +| `server.prompt()` | `server.registerPrompt()` | |
| 42 | + |
| 43 | +### Resource Templates |
| 44 | + |
| 45 | +Use `ResourceTemplate` for parameterized resource URIs: |
| 46 | + |
| 47 | +```typescript |
| 48 | +import { ResourceTemplate } from '@modelcontextprotocol/sdk/server/mcp.js'; |
| 49 | + |
| 50 | +// Static resource - fixed URI |
| 51 | +server.registerResource('config', 'config://app', { ... }, handler); |
| 52 | + |
| 53 | +// Dynamic resource - parameterized URI |
| 54 | +server.registerResource( |
| 55 | + 'user-profile', |
| 56 | + new ResourceTemplate('users://{userId}/profile', { list: undefined }), |
| 57 | + { title: 'User Profile', description: '...' }, |
| 58 | + async (uri, variables) => { |
| 59 | + const userId = variables.userId as string; |
| 60 | + // ... |
| 61 | + } |
| 62 | +); |
| 63 | +``` |
| 64 | + |
| 65 | +### Error Handling |
| 66 | + |
| 67 | +Use `isError: true` for tool execution failures: |
| 68 | + |
| 69 | +```typescript |
| 70 | +return { |
| 71 | + content: [{ type: 'text', text: 'Error: Something went wrong' }], |
| 72 | + isError: true |
| 73 | +}; |
| 74 | +``` |
| 75 | + |
17 | 76 | Adopting this guide will make the tools more predictable and easier for both humans and AI agents to understand and use correctly. |
0 commit comments