craft-mcp-wrapper

mattymil/craft-mcp-wrapper
4 starsMITCommunity

Install to Claude Code

This server doesn't publish a one-line install command. Follow the setup in the source repository.

Summary

Provides a unified MCP interface to search and read content from multiple Craft documents simultaneously, aggregating results across configured documents while handling failures gracefully.

README.md

Craft MCP Wrapper

![License: MIT](https://opensource.org/licenses/MIT) ![Node.js](https://nodejs.org/) ![TypeScript](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 | 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 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:
   git clone https://github.com/mattymil/craft-mcp-wrapper.git
   cd craft-mcp-wrapper
  1. Install dependencies:
   npm install
  1. Configure your Craft documents (see Configuration section below)
  1. 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:

  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:

# 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

  1. AWS CLI configured:
   aws configure

Provide your AWS Access Key ID, Secret Access Key, and default region.

  1. 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 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

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

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:

---

Built with:

Star History

If you find this project useful, please consider giving it a ⭐ on GitHub!

Related Projects

Related MCP servers

Browse all β†’