craft-mcp-wrapper
by mattymil
README.md
# Craft MCP Wrapper
[](https://opensource.org/licenses/MIT)
[](https://nodejs.org/)
[](https://www.typescriptlang.org/)
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](QUICKSTART.md) | [Contributing](CONTRIBUTING.md) | [AWS Deployment](DEPLOYMENT.md) | [Status](DEPLOYMENT_STATUS.md)
## 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 documents
- `search_all_notes` - Search across all documents with aggregation
- `search_document` - Search within a specific document
- `read_document` - Read entire document structure
- `read_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
1. **Clone the repository:**
```bash
git clone https://github.com/mattymil/craft-mcp-wrapper.git
cd craft-mcp-wrapper
```
2. **Install dependencies:**
```bash
npm install
```
3. **Configure your Craft documents** (see Configuration section below)
4. **Build the project:**
```bash
npm run build
```
### Production Deployment (macOS)
For stable production use with MCP clients, deploy to a system-wide location:
```bash
# 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:
```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"
}
]
}
```
**To add more documents:**
1. Get the Craft share link for your document
2. Add `/api/v1` to the end of the link
3. Add a new entry with a friendly name and the API endpoint
### Environment Variables (`.env`)
Copy `.env.example` to `.env` and configure as needed:
```bash
# 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):
```bash
npm start
```
This starts the server in stdio mode, communicating via standard input/output.
#### SSE Mode (HTTP Server)
For remote connections or testing:
```bash
npm run start:sse
```
Or explicitly:
```bash
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:
```bash
npm run offline
```
This starts a local API Gateway emulator on `http://localhost:3000`
#### Development Mode
Auto-reload on file changes:
```bash
# 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
1. **AWS CLI configured:**
```bash
aws configure
```
Provide your AWS Access Key ID, Secret Access Key, and default region.
2. **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
```bash
npm run info
```
#### View Lambda Logs
```bash
# Tail logs in real-time
npm run logs
# Stage-specific logs
npm run logs:dev
npm run logs:prod
```
#### Remove Lambda Deployment
```bash
# 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:
```bash
# 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
```bash
curl https://{api-id}.execute-api.{region}.amazonaws.com/health
```
- `GET /tools` - List available tools
```bash
curl https://{api-id}.execute-api.{region}.amazonaws.com/tools
```
- `POST /tools/call` - Execute a tool
```bash
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:
```json
{
"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):
```json
{
"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:
```json
{
"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 pattern
- `caseSensitive` (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 document
- `query` (string, required) - Search pattern
- `caseSensitive` (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 document
- `maxDepth` (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 document
- `blockId` (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
1. **Use Specific Searches:** Prefer `search_document` over `search_all_notes` when you know which document contains the data
2. **Limit Depth:** Use `maxDepth` parameter with `read_document` to avoid fetching entire deep hierarchies
3. **Monitor Truncation:** Watch stderr for `[WARN] Response truncated` messages
4. **Adjust Size Limits:** Increase `MAX_RESPONSE_SIZE` if you frequently see truncation warnings
5. **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 `_metadata` field in the response indicating truncation
- Preserved top-level structure with truncated arrays/content
**Solutions:**
- Increase `MAX_RESPONSE_SIZE` in your `.env` file
- Use more specific queries to reduce result set size
- Use `read_block` instead of `read_document` for specific content
- Reduce `maxDepth` when reading documents
## Testing
### Run Test Suite
```bash
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:
1. Create parallel promises for each configured document
2. Use `Promise.allSettled()` to handle failures gracefully
3. Collect successful results with document name context
4. Include error messages for failed requests
5. 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.json` exists 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 `.env` file
**"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.ts` if 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_SIZE` is 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_SIZE` and was automatically truncated
- Check the `_metadata` field in responses for truncation details
- **To fix:**
- Increase `MAX_RESPONSE_SIZE` in `.env` (e.g., `MAX_RESPONSE_SIZE=2097152` for 2MB)
- Use more specific queries to reduce result size
- Use `search_document` instead of `search_all_notes`
- Limit `maxDepth` when calling `read_document`
- **Note:** Setting `MAX_RESPONSE_SIZE` too 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:
1. Fork the repository
2. Create a feature branch (`git checkout -b feature/amazing-feature`)
3. Commit your changes (`git commit -m 'Add amazing feature'`)
4. Push to the branch (`git push origin feature/amazing-feature`)
5. Open a Pull Request
### Development Setup
```bash
git clone https://github.com/mattymil/craft-mcp-wrapper.git
cd craft-mcp-wrapper
npm install
npm run build
```
### Running Tests
```bash
npm run test
```
## Support
If you encounter any issues or have questions:
- Open an issue on [GitHub](https://github.com/mattymil/craft-mcp-wrapper/issues)
- Check existing issues for solutions
- Review the [Troubleshooting](#troubleshooting) section
---
**Built with:**
- [Model Context Protocol](https://modelcontextprotocol.io) by Anthropic
- [Craft.do](https://craft.do) Document API
- TypeScript, Express, Axios, Zod
## Star History
If you find this project useful, please consider giving it a β on GitHub!
## Related Projects
- [MCP Specification](https://github.com/modelcontextprotocol/specification)
- [MCP TypeScript SDK](https://github.com/modelcontextprotocol/typescript-sdk)
- [Craft API Documentation](https://support.craft.do/hc/en-us/articles/360017505798-API-Documentation)
This server cannot be deployed
Maintenance
ActivityInactive
ResponsivenessNo issues