* 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>
250 lines
7.2 KiB
Markdown
250 lines
7.2 KiB
Markdown
# 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:
|
|
```json
|
|
{
|
|
"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:
|
|
```typescript
|
|
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
|