Craft MCP Wrapper
  
A Model Context Protocol (MCP) server that wraps multiple Craft document APIs and makes them accessible to AI assistants like Perplexity AI, Claude Desktop, VS Code, and Cursor.
Note: This is an open-source project. Contributions are welcome!
π Quick Links: Quick Start | Contributing | AWS Deployment | Status
Overview
This MCP server provides a unified interface to search and read content from multiple Craft documents simultaneously. It aggregates results across configured documents while gracefully handling failures, making it ideal for querying distributed knowledge bases.
Features
- 5 MCP Tools:
list_documents- List all configured Craft documentssearch_all_notes- Search across all documents with aggregationsearch_document- Search within a specific documentread_document- Read entire document structureread_block- Read a specific block by ID
- Dual Transport Modes:
- Stdio Mode - For local AI assistants (Perplexity local, Claude Desktop)
- SSE Mode - HTTP/SSE transport for remote connections
- Robust Error Handling:
- Graceful degradation when individual APIs fail
- Partial results with error context
- Input validation with Zod schemas
- Easy Configuration:
- JSON-based document configuration
- Environment variables for server settings
- No authentication required for Craft public share links
Installation
- Clone the repository:
git clone https://github.com/mattymil/craft-mcp-wrapper.git
cd craft-mcp-wrapper
- Install dependencies:
npm install
- Configure your Craft documents (see Configuration section below)
- Build the project:
npm run build
Production Deployment (macOS)
For stable production use with MCP clients, deploy to a system-wide location:
# Build the project
npm run build
# Deploy to production location
sudo mkdir -p /usr/local/lib/craft-wrapper
sudo cp -r build config.json package.json node_modules /usr/local/lib/craft-wrapper/
Then configure your MCP clients to use /usr/local/lib/craft-wrapper/build/index.js as the entry point.
Benefits:
- Stable path that won't change with project updates
- Separates production runtime from development workspace
- Easy rollback by keeping previous versions
Configuration
Document Configuration (config.json)
Edit config.json to add your Craft document share links:
{
"documents": [
{
"name": "My Notes",
"apiEndpoint": "https://connect.craft.do/links/YOUR_SHARE_LINK_1/api/v1"
},
{
"name": "Project Documentation",
"apiEndpoint": "https://connect.craft.do/links/YOUR_SHARE_LINK_2/api/v1"
}
]
}
To add more documents:
- Get the Craft share link for your document
- Add
/api/v1to the end of the link - Add a new entry with a friendly name and the API endpoint
Environment Variables (.env)
Copy .env.example to .env and configure as needed:
# Transport mode: "stdio" or "sse"
MCP_TRANSPORT=stdio
# SSE mode configuration (only used when MCP_TRANSPORT=sse)
PORT=3000
SSE_ENDPOINT=/sse
# Optional: API key for SSE authentication
# MCP_API_KEY=your-secret-key-here
# Performance tuning
# Maximum response size in bytes (default: 1048576 = 1MB)
# Larger values may cause stdio blocking with slow connections
MAX_RESPONSE_SIZE=1048576
Performance Configuration:
MAX_RESPONSE_SIZE- Maximum size of JSON responses in bytes (default: 1MB)- Responses larger than this will be automatically truncated
- Increase for large document reads, but be aware of stdio blocking
- Decrease for faster performance with limited bandwidth
Running the Server
Local Development
Stdio Mode (Default)
For local AI assistants like Claude Desktop or Perplexity (local):
npm start
This starts the server in stdio mode, communicating via standard input/output.
SSE Mode (HTTP Server)
For remote connections or testing:
npm run start:sse
Or explicitly:
node build/index.js --sse
The server will start on port 3000 (configurable via PORT env variable) with:
- SSE endpoint:
http://localhost:3000/sse - Messages endpoint:
http://localhost:3000/messages - Health check:
http://localhost:3000/health
Local Lambda Testing
Test Lambda function locally with serverless-offline:
npm run offline
This starts a local API Gateway emulator on http://localhost:3000
Development Mode
Auto-reload on file changes:
# Stdio mode
npm run dev
# SSE mode
npm run dev:sse
AWS Lambda Deployment
Deploy the server as an AWS Lambda function with API Gateway.
Prerequisites
- AWS CLI configured:
aws configure
Provide your AWS Access Key ID, Secret Access Key, and default region.
- AWS credentials: Ensure you have permissions to create:
- Lambda functions
- API Gateway HTTP APIs
- CloudWatch Logs
- IAM roles
Deploy to AWS
Deploy to default stage (dev): ``bash npm run deploy ``
Deploy to specific stages: ```bash
Development
npm run deploy:dev
Production
npm run deploy:prod ```
After deployment, Serverless Framework will output:
- API Gateway endpoint URL
- Lambda function name
- CloudFormation stack name
View Deployment Info
npm run info
View Lambda Logs
# Tail logs in real-time
npm run logs
# Stage-specific logs
npm run logs:dev
npm run logs:prod
Remove Lambda Deployment
# Remove default stage
npm run remove
# Remove specific stages
npm run remove:dev
npm run remove:prod
Environment Variables for Lambda
Set environment variables in serverless.yml or via command line:
# Set API key for authentication
export MCP_API_KEY="your-secret-key"
npm run deploy
Or edit serverless.yml: ``yaml provider: environment: MCP_API_KEY: ${env:MCP_API_KEY, 'default-key'} ``
Lambda Configuration
Default settings in serverless.yml:
- Runtime: Node.js 20.x
- Memory: 512 MB
- Timeout: 30 seconds
- Region: us-east-1
Modify these in serverless.yml as needed.
API Gateway Endpoints
After deployment, your Lambda provides a REST API at:
https://{api-id}.execute-api.{region}.amazonaws.com/
Endpoints:
GET /health- Health check
curl https://{api-id}.execute-api.{region}.amazonaws.com/health
GET /tools- List available tools
curl https://{api-id}.execute-api.{region}.amazonaws.com/tools
POST /tools/call- Execute a tool
curl -X POST https://{api-id}.execute-api.{region}.amazonaws.com/tools/call \
-H "Content-Type: application/json" \
-d '{"name": "list_documents", "arguments": {}}'
Note: The Lambda deployment uses a simple REST API rather than the full MCP protocol. For MCP protocol support (required by Perplexity), use the local stdio or SSE server.
Cost Estimates
AWS Lambda:
- Free tier: 1M requests/month + 400,000 GB-seconds compute
- After free tier: $0.20 per 1M requests + $0.0000166667 per GB-second
API Gateway:
- Free tier: None
- HTTP API: $1.00 per million requests
Example: 10,000 requests/month with 512MB, 3s avg execution:
- Lambda: Free (within free tier)
- API Gateway: $0.01/month
- Total: ~$0.01/month
Lambda Limitations
- Cold starts: First request after idle period may be slower (1-2 seconds)
- REST API only: Lambda provides a REST API, not full MCP protocol (use local server for MCP clients)
- No stdio mode: Stdio mode is not supported in Lambda (serverless environment)
- No SSE/streaming: Lambda REST API uses request/response, not Server-Sent Events
- Timeout: Maximum 30 seconds (configurable up to 15 minutes)
Connecting AI Assistants
Perplexity AI
β οΈ Important: Perplexity requires the full MCP protocol via stdio mode. Use the local server, not Lambda.
Stdio Configuration: Add to Perplexity's MCP settings:
{
"mcpServers": {
"craft-wrapper": {
"command": "node",
"args": ["/usr/local/lib/craft-wrapper/build/index.js"]
}
}
}
For development/custom installations, replace with your project path: ``json { "mcpServers": { "craft-wrapper": { "command": "node", "args": ["/path/to/your/craft-mcp-wrapper/build/index.js"] } } } ``
Note: The MCP_TRANSPORT environment variable defaults to stdio, so you don't need to specify it unless you've changed the default in your .env file.
Claude Desktop
Add to ~/Library/Application Support/Claude/claude_desktop_config.json (macOS):
{
"mcpServers": {
"craft-wrapper": {
"command": "node",
"args": ["/usr/local/lib/craft-wrapper/build/index.js"]
}
}
}
For development/custom installations, replace with your project path.
VS Code / Cursor
For MCP-compatible extensions, configure the server path in your workspace settings:
{
"mcp.servers": {
"craft-wrapper": {
"command": "node",
"args": ["/usr/local/lib/craft-wrapper/build/index.js"]
}
}
}
For development/custom installations, replace with your project path.
Remote SSE Connection
If using SSE mode remotely (local server only):
Server URL: http://your-server:3000/sse
With authentication (if MCP_API_KEY is set): `` http://your-server:3000/sse?api_key=your-secret-key-here ``
Lambda REST API (For Custom Integrations)
The Lambda deployment provides a REST API for custom integrations that don't require the MCP protocol:
Base URL: https://{api-id}.execute-api.{region}.amazonaws.com
Example - List Documents: ``bash curl -X POST https://YOUR-API-ID.execute-api.us-east-1.amazonaws.com/tools/call \ -H "Content-Type: application/json" \ -d '{"name": "list_documents", "arguments": {}}' ``
Example - Search All Notes: ``bash curl -X POST https://YOUR-API-ID.execute-api.us-east-1.amazonaws.com/tools/call \ -H "Content-Type: application/json" \ -d '{ "name": "search_all_notes", "arguments": { "query": "leadership", "caseSensitive": false } }' ``
Response Format: ``json { "success": true, "result": { // Tool-specific result data } } ``
Tools Documentation
1. list_documents
Lists all configured Craft documents.
Parameters: None
Example Response: ``json { "documents": [ { "name": "My Notes", "apiEndpoint": "https://connect.craft.do/links/YOUR_SHARE_LINK_1/api/v1" }, { "name": "Project Documentation", "apiEndpoint": "https://connect.craft.do/links/YOUR_SHARE_LINK_2/api/v1" } ], "count": 2 } ``
Use Case: Discover available documents before searching.
2. search_all_notes
Search across all configured documents simultaneously.
Parameters:
query(string, required) - Search patterncaseSensitive(boolean, optional) - Case-sensitive search (default: false)
Example JSON-RPC Request: ``json { "jsonrpc": "2.0", "method": "tools/call", "params": { "name": "search_all_notes", "arguments": { "query": "leadership", "caseSensitive": false } }, "id": 1 } ``
Example Response: ``json { "query": "leadership", "caseSensitive": false, "totalResults": 5, "documentsSearched": 2, "results": [ { "documentName": "Notes", "results": [ { "block": { "id": "...", "content": "..." }, "documentName": "Notes" } ] }, { "documentName": "Bonhoeffer Notes", "results": [...] } ] } ``
Use Case: Find content across your entire Craft knowledge base without knowing which document contains it.
3. search_document
Search within a specific Craft document.
Parameters:
documentName(string, required) - Name of the documentquery(string, required) - Search patterncaseSensitive(boolean, optional) - Case-sensitive search (default: false)
Example JSON-RPC Request: ``json { "jsonrpc": "2.0", "method": "tools/call", "params": { "name": "search_document", "arguments": { "documentName": "Notes", "query": "meeting notes", "caseSensitive": false } }, "id": 2 } ``
Use Case: Targeted search when you know which document contains the information.
4. read_document
Read the entire structure of a Craft document.
Parameters:
documentName(string, required) - Name of the documentmaxDepth(number, optional) - Maximum depth of block hierarchy
Example JSON-RPC Request: ``json { "jsonrpc": "2.0", "method": "tools/call", "params": { "name": "read_document", "arguments": { "documentName": "Notes", "maxDepth": 3 } }, "id": 3 } ``
Use Case: Retrieve complete document structure for analysis or export.
5. read_block
Read a specific block by its ID.
Parameters:
documentName(string, required) - Name of the documentblockId(string, required) - ID of the block
Example JSON-RPC Request: ``json { "jsonrpc": "2.0", "method": "tools/call", "params": { "name": "read_block", "arguments": { "documentName": "Notes", "blockId": "block-123-abc" } }, "id": 4 } ``
Use Case: Retrieve specific content when you have a block ID from a previous search.
Performance Best Practices
Stdio Performance Optimization
This server is optimized for fast stdio communication with AI assistants like Perplexity:
- Compact JSON: Response formatting (pretty-printing) is disabled to reduce payload size by ~40%
- Response Size Limits: Large responses are automatically truncated to prevent stdio buffer blocking
- Performance Logging: All tool executions log timing and size metrics to stderr for monitoring
Performance Metrics
Check stderr output for performance data: `` [PERF] 2024-01-15T10:30:45.123Z search_all_notes 245ms size=15234bytes [PERF] 2024-01-15T10:30:50.456Z read_document 1200ms size=524288bytes ``
Tips for Optimal Performance
- Use Specific Searches: Prefer
search_documentoversearch_all_noteswhen you know which document contains the data - Limit Depth: Use
maxDepthparameter withread_documentto avoid fetching entire deep hierarchies - Monitor Truncation: Watch stderr for
[WARN] Response truncatedmessages - Adjust Size Limits: Increase
MAX_RESPONSE_SIZEif you frequently see truncation warnings - Query Optimization: Use specific search patterns instead of broad queries across all documents
When Responses Are Truncated
If a response exceeds MAX_RESPONSE_SIZE, you'll see:
- A
[WARN]message in stderr with original and truncated sizes - A
_metadatafield in the response indicating truncation - Preserved top-level structure with truncated arrays/content
Solutions:
- Increase
MAX_RESPONSE_SIZEin your.envfile - Use more specific queries to reduce result set size
- Use
read_blockinstead ofread_documentfor specific content - Reduce
maxDepthwhen reading documents
Testing
Run Test Suite
npm run build
npm run test
The test suite validates:
- All 5 tools with valid inputs
- Error handling with invalid inputs
- Configuration loading
- API response structures
Manual Testing (SSE Mode)
Start the server: ``bash npm run start:sse ``
Test health endpoint: ``bash curl http://localhost:3000/health ``
Test SSE connection: ``bash curl http://localhost:3000/sse ``
List available tools: ``bash curl -X POST http://localhost:3000/messages \ -H "Content-Type: application/json" \ -d '{ "jsonrpc": "2.0", "method": "tools/list", "id": 1 }' ``
Architecture
High-Level Design
βββββββββββββββββββββββ
β AI Assistant β
β (Perplexity/Claude) β
ββββββββββββ¬βββββββββββ
β MCP Protocol
β (stdio or SSE)
ββββββββββββ΄βββββββββββ
β MCP Server β
β (craft-wrapper) β
β β
β βββββββββββββββββ β
β β Tool Registry β β
β βββββββββ¬ββββββββ β
β β β
β βββββββββ΄ββββββββ β
β β Craft API β β
β β Client β β
β β (axios) β β
β βββββββββ¬ββββββββ β
ββββββββββββΌβββββββββββ
β
ββββββββ΄ββββββββ
β β
βββββ΄βββββ ββββββ΄βββ
βCraft β βCraft β
βDoc 1 β βDoc 2 β
ββββββββββ βββββββββ
Aggregation Strategy
When search_all_notes is called:
- Create parallel promises for each configured document
- Use
Promise.allSettled()to handle failures gracefully - Collect successful results with document name context
- Include error messages for failed requests
- Return aggregated results with summary statistics
Error Handling
- Invalid document names: Returns error with list of available documents
- Network failures: Caught and returned as structured errors
- Malformed responses: Wrapped in error objects
- Partial failures: Results from successful APIs still returned
Troubleshooting
Common Issues
"Failed to load config.json"
- Ensure
config.jsonexists in the project root - Verify JSON syntax is valid
- Check that all required fields are present
"Port 3000 already in use" (SSE mode)
- Change the port:
PORT=3001 npm run start:sse - Or update
.envfile
"No active SSE connection found"
- Ensure you've opened the SSE endpoint (
/sse) before sending messages - Check that the connection hasn't been closed
Network errors / timeout
- Verify Craft share links are still valid
- Check internet connectivity
- Increase timeout in
craft-api.tsif needed (currently 30s)
Tools not appearing in AI assistant
- Rebuild the project:
npm run build - Restart the AI assistant
- Check server logs for errors (stderr in stdio mode)
Slow performance with Perplexity/Claude
- Check stderr for
[PERF]logs to identify slow operations - Large responses (>500KB) may cause delays - see "Response truncated" guidance below
- Verify
MAX_RESPONSE_SIZEis set appropriately (default 1MB) - Use more specific search queries instead of broad searches
"Response truncated" warnings in stderr
- This means the response exceeded
MAX_RESPONSE_SIZEand was automatically truncated - Check the
_metadatafield in responses for truncation details - To fix:
- Increase
MAX_RESPONSE_SIZEin.env(e.g.,MAX_RESPONSE_SIZE=2097152for 2MB) - Use more specific queries to reduce result size
- Use
search_documentinstead ofsearch_all_notes - Limit
maxDepthwhen callingread_document - Note: Setting
MAX_RESPONSE_SIZEtoo high may cause stdio blocking
Known Limitations
- Lambda = REST API only: The Lambda deployment provides a REST API, not the full MCP protocol. For MCP clients (Perplexity, Claude Desktop), use the local stdio/SSE server.
- Perplexity requires local server: Perplexity's MCP connector requires stdio mode, which only works with the local server.
- Read-only: This MVP only supports READ operations (search, fetch). Write operations (insert, update, delete) are not implemented.
- Authentication: Craft APIs must be publicly accessible share links. Private documents with authentication are not supported yet.
- Rate limiting: No built-in rate limiting. Consider adding if making many requests.
License
MIT
Contributing
Contributions are welcome! Here's how you can help:
- Fork the repository
- Create a feature branch (
git checkout -b feature/amazing-feature) - Commit your changes (
git commit -m 'Add amazing feature') - Push to the branch (
git push origin feature/amazing-feature) - Open a Pull Request
Development Setup
git clone https://github.com/mattymil/craft-mcp-wrapper.git
cd craft-mcp-wrapper
npm install
npm run build
Running Tests
npm run test
Support
If you encounter any issues or have questions:
- Open an issue on GitHub
- Check existing issues for solutions
- Review the Troubleshooting section
---
Built with:
- Model Context Protocol by Anthropic
- Craft.do Document API
- TypeScript, Express, Axios, Zod
Star History
If you find this project useful, please consider giving it a β on GitHub!






