Files
accounted/.claude/skills/mcp-builder/reference/mcp_best_practices.md
T
Jakob WennbergandClaude Opus 4.6 5d66dd6bfc feat: MCP server, API keys, OAuth, and KPI dashboard (#72)
* fix: prevent Chrome auto-translate from crashing React during onboarding

Chrome auto-translate modifies DOM text nodes when it detects a Swedish
page (lang="sv") in a browser set to English. React does not expect
external DOM mutations and throws, crashing the entire component tree
into global-error.tsx on every step transition.

Add translate="no" and <meta name="google" content="notranslate"> to
suppress browser translation. Also fix timezone-unsafe date parsing in
fiscal period validation (new Date("YYYY-MM-DD") + getDate() returns
local-timezone values, shifting dates by -1 day in Western timezones).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix: add notranslate meta tag to global-error.tsx for consistency

Per review feedback — global-error.tsx renders its own <html> document,
so it needs the same <meta name="google" content="notranslate"> tag as
layout.tsx to fully suppress Chrome translation on error pages.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* feat: add MCP server extension with OAuth, API keys, and KPI dashboard

Let users do bookkeeping through Claude Desktop, Claude Code, or any
MCP-compatible client. "Show my uncategorized transactions." "Book that
as office supplies." "Invoice Acme for 15,000 kr."

MCP server (extension):
- 10 tools: transactions, categorization, customers, invoices,
  trial balance, VAT report, KPI report, income statement
- JSON-RPC 2.0 protocol (no SDK dependency, works in serverless)
- Tool annotations, pagination, input validation per MCP best practices
- Same engine as web UI (VAT rules, exchange rates, event emission)

API key infrastructure (core):
- api_keys table with RLS, rate limiting (100 RPM), scopes column
- Atomic rate limit via DB RPC (validate_and_increment_api_key)
- Key management API routes + settings UI panel

OAuth 2.1 for Claude Desktop connectors:
- .well-known/oauth-protected-resource + oauth-authorization-server
- Authorization endpoint with consent page
- Token endpoint with PKCE verification
- Stateless encrypted auth codes (AES-256-GCM, no DB storage)
- Dynamic client registration

KPI dashboard:
- /nyckeltal page with hero cards, operational grid, trend chart
- GET /api/reports/kpi endpoint
- Gross margin, cash position, expense ratio, avg payment days,
  VAT liability, revenue/expense trend

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix: address OAuth security vulnerabilities from code review

Critical fixes:
- Auth code replay: Track used codes in oauth_used_codes table with
  unique constraint. Codes are single-use per OAuth 2.1 §4.1.2.
- Open redirect: Validate redirect_uri against hardcoded allowlist
  of known Claude callback URLs + localhost for dev.

P1 fixes:
- Move API key creation from /authorize to /token endpoint. Keys are
  only created after PKCE verification, preventing orphaned keys on
  abandoned OAuth flows.
- Add ensureInitialized() to MCP server so event handlers load and
  transaction.categorized events reach extensions.

P2 fixes:
- Remove 'plain' from PKCE methods — only S256 is advertised and
  accepted.
- Fix extension count in sectors test (10 → 11 for mcp-server).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix: remove duplicate ensureInitialized() that caused circular import

The extension router (ext/[...path]/route.ts) already calls
ensureInitialized() before dispatching to handlers. The duplicate
call in server.ts created a circular import that Turbopack couldn't
resolve, breaking the Vercel build.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-21 12:57:14 +01:00

7.2 KiB

MCP Server Best Practices

Quick Reference

Server Naming

  • Python: {service}_mcp (e.g., slack_mcp)
  • Node/TypeScript: {service}-mcp-server (e.g., slack-mcp-server)

Tool Naming

  • Use snake_case with service prefix
  • Format: {service}_{action}_{resource}
  • Example: slack_send_message, github_create_issue

Response Formats

  • Support both JSON and Markdown formats
  • JSON for programmatic processing
  • Markdown for human readability

Pagination

  • Always respect limit parameter
  • Return has_more, next_offset, total_count
  • Default to 20-50 items

Transport

  • Streamable HTTP: For remote servers, multi-client scenarios
  • stdio: For local integrations, command-line tools
  • Avoid SSE (deprecated in favor of streamable HTTP)

Server Naming Conventions

Follow these standardized naming patterns:

Python: Use format {service}_mcp (lowercase with underscores)

  • Examples: slack_mcp, github_mcp, jira_mcp

Node/TypeScript: Use format {service}-mcp-server (lowercase with hyphens)

  • Examples: slack-mcp-server, github-mcp-server, jira-mcp-server

The name should be general, descriptive of the service being integrated, easy to infer from the task description, and without version numbers.


Tool Naming and Design

Tool Naming

  1. Use snake_case: search_users, create_project, get_channel_info
  2. Include service prefix: Anticipate that your MCP server may be used alongside other MCP servers
    • Use slack_send_message instead of just send_message
    • Use github_create_issue instead of just create_issue
  3. Be action-oriented: Start with verbs (get, list, search, create, etc.)
  4. Be specific: Avoid generic names that could conflict with other servers

Tool Design

  • Tool descriptions must narrowly and unambiguously describe functionality
  • Descriptions must precisely match actual functionality
  • Provide tool annotations (readOnlyHint, destructiveHint, idempotentHint, openWorldHint)
  • Keep tool operations focused and atomic

Response Formats

All tools that return data should support multiple formats:

JSON Format (response_format="json")

  • Machine-readable structured data
  • Include all available fields and metadata
  • Consistent field names and types
  • Use for programmatic processing

Markdown Format (response_format="markdown", typically default)

  • Human-readable formatted text
  • Use headers, lists, and formatting for clarity
  • Convert timestamps to human-readable format
  • Show display names with IDs in parentheses
  • Omit verbose metadata

Pagination

For tools that list resources:

  • Always respect the limit parameter
  • Implement pagination: Use offset or cursor-based pagination
  • Return pagination metadata: Include has_more, next_offset/next_cursor, total_count
  • Never load all results into memory: Especially important for large datasets
  • Default to reasonable limits: 20-50 items is typical

Example pagination response:

{
  "total": 150,
  "count": 20,
  "offset": 0,
  "items": [...],
  "has_more": true,
  "next_offset": 20
}

Transport Options

Streamable HTTP

Best for: Remote servers, web services, multi-client scenarios

Characteristics:

  • Bidirectional communication over HTTP
  • Supports multiple simultaneous clients
  • Can be deployed as a web service
  • Enables server-to-client notifications

Use when:

  • Serving multiple clients simultaneously
  • Deploying as a cloud service
  • Integration with web applications

stdio

Best for: Local integrations, command-line tools

Characteristics:

  • Standard input/output stream communication
  • Simple setup, no network configuration needed
  • Runs as a subprocess of the client

Use when:

  • Building tools for local development environments
  • Integrating with desktop applications
  • Single-user, single-session scenarios

Note: stdio servers should NOT log to stdout (use stderr for logging)

Transport Selection

Criterion stdio Streamable HTTP
Deployment Local Remote
Clients Single Multiple
Complexity Low Medium
Real-time No Yes

Security Best Practices

Authentication and Authorization

OAuth 2.1:

  • Use secure OAuth 2.1 with certificates from recognized authorities
  • Validate access tokens before processing requests
  • Only accept tokens specifically intended for your server

API Keys:

  • Store API keys in environment variables, never in code
  • Validate keys on server startup
  • Provide clear error messages when authentication fails

Input Validation

  • Sanitize file paths to prevent directory traversal
  • Validate URLs and external identifiers
  • Check parameter sizes and ranges
  • Prevent command injection in system calls
  • Use schema validation (Pydantic/Zod) for all inputs

Error Handling

  • Don't expose internal errors to clients
  • Log security-relevant errors server-side
  • Provide helpful but not revealing error messages
  • Clean up resources after errors

DNS Rebinding Protection

For streamable HTTP servers running locally:

  • Enable DNS rebinding protection
  • Validate the Origin header on all incoming connections
  • Bind to 127.0.0.1 rather than 0.0.0.0

Tool Annotations

Provide annotations to help clients understand tool behavior:

Annotation Type Default Description
readOnlyHint boolean false Tool does not modify its environment
destructiveHint boolean true Tool may perform destructive updates
idempotentHint boolean false Repeated calls with same args have no additional effect
openWorldHint boolean true Tool interacts with external entities

Important: Annotations are hints, not security guarantees. Clients should not make security-critical decisions based solely on annotations.


Error Handling

  • Use standard JSON-RPC error codes
  • Report tool errors within result objects (not protocol-level errors)
  • Provide helpful, specific error messages with suggested next steps
  • Don't expose internal implementation details
  • Clean up resources properly on errors

Example error handling:

try {
  const result = performOperation();
  return { content: [{ type: "text", text: result }] };
} catch (error) {
  return {
    isError: true,
    content: [{
      type: "text",
      text: `Error: ${error.message}. Try using filter='active_only' to reduce results.`
    }]
  };
}

Testing Requirements

Comprehensive testing should cover:

  • Functional testing: Verify correct execution with valid/invalid inputs
  • Integration testing: Test interaction with external systems
  • Security testing: Validate auth, input sanitization, rate limiting
  • Performance testing: Check behavior under load, timeouts
  • Error handling: Ensure proper error reporting and cleanup

Documentation Requirements

  • Provide clear documentation of all tools and capabilities
  • Include working examples (at least 3 per major feature)
  • Document security considerations
  • Specify required permissions and access levels
  • Document rate limits and performance characteristics