diff --git a/CHANGELOG.md b/CHANGELOG.md index e233add..8aa042a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -14,10 +14,16 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Multi-platform Docker builds (linux/amd64, linux/arm64) - Comprehensive MCP protocol testing endpoints - Enhanced documentation with MCP protocol details +- Dedicated Claude Desktop integration section in README +- STDIO mode usage examples and testing instructions +- STDIO test suite for verifying MCP protocol functionality ### Changed - Updated README with GitHub Container Registry usage - Improved LibreChat integration documentation +- Reorganized README to prioritize STDIO/Claude Desktop usage +- Enhanced prerequisites section with mode-specific requirements +- Updated feature list to highlight Claude Desktop compatibility - Enhanced Docker Compose configuration ### Fixed diff --git a/README.md b/README.md index c340963..acca04d 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # QuiverAPI MCP Server -A Model Context Protocol (MCP) server that provides access to QuiverAPI's Tier 1 endpoints for financial and political data. Designed for integration with LibreChat and other MCP clients. +A Model Context Protocol (MCP) server that provides access to QuiverAPI's Tier 1 endpoints for financial and political data. Works seamlessly with Claude Desktop, LibreChat, and other MCP clients. [![Docker Build](https://github.com/usnavy13/quiverMCP/actions/workflows/docker-publish.yml/badge.svg)](https://github.com/usnavy13/quiverMCP/actions/workflows/docker-publish.yml) [![Docker Pulls](https://img.shields.io/docker/pulls/ghcr.io/usnavy13/quivermcp)](https://github.com/usnavy13/quiverMCP/pkgs/container/quivermcp) @@ -15,7 +15,8 @@ This server implements the complete Model Context Protocol specification, includ - ✅ **Prompts**: `prompts/list`, `prompts/get` (extensible) - ✅ **Utilities**: `$/cancelRequest`, health checks - ✅ **Capability Negotiation**: Full feature discovery -- ✅ **LibreChat Compatible**: Streamable HTTP transport +- ✅ **Claude Desktop Ready**: Native STDIO transport +- ✅ **LibreChat Compatible**: HTTP transport available ## 🚀 Features @@ -30,9 +31,10 @@ This MCP server provides access to 21 Tier 1 endpoints from QuiverAPI, including ## 📋 Prerequisites -- Docker and Docker Compose (recommended) -- OR Node.js 18+ (for manual installation) -- A valid QuiverAPI token ([Get one here](https://www.quiverquant.com/)) +- **For Claude Desktop**: Just a QuiverAPI token! (npx handles the rest) +- **For manual setup**: Node.js 18+ +- **For Docker**: Docker and Docker Compose +- **QuiverAPI token**: [Get one here](https://www.quiverquant.com/) (required for all modes) ## 🐳 Docker Installation (Recommended) @@ -121,6 +123,51 @@ PORT=3000 LIBRECHAT_ORIGIN=http://localhost:3080 ``` +## 🤖 Claude Desktop Integration (Recommended) + +### Quick Setup for Claude Desktop + +The easiest way to use QuiverAPI with Claude Desktop: + +1. **Get your API token** from [QuiverQuant.com](https://www.quiverquant.com/) + +2. **Add to your Claude Desktop config file**: + + **macOS**: `~/Library/Application Support/Claude/claude_desktop_config.json` + **Windows**: `%APPDATA%\Claude\claude_desktop_config.json` + + ```json + { + "mcpServers": { + "quiver": { + "command": "npx", + "args": ["-y", "github:usnavy13/quiverMCP"], + "env": { + "QUIVER_API_TOKEN": "your_token_here" + } + } + } + } + ``` + +3. **Restart Claude Desktop** and start using QuiverAPI tools in your conversations! + +### Example Usage in Claude + +Once configured, you can ask Claude things like: + +``` +Show me recent Congress trading activity for AAPL +``` + +``` +What are the latest government contracts awarded to Microsoft? +``` + +``` +Get lobbying data for Tesla over the past year +``` + ## 🤖 LibreChat Integration ### Step 1: Configure LibreChat @@ -325,13 +372,69 @@ npm run watch This server supports two transport modes: -1. **HTTP Mode** (default): For LibreChat and web-based MCP clients - - Endpoint: `http://localhost:3000/message` - - Start with: `npm start` +### 1. **STDIO Mode**: For Claude Desktop and MCP Clients + +**Recommended for most users** - Direct MCP protocol communication via stdin/stdout. + +#### Quick Start with Claude Desktop + +1. **Get your QuiverAPI token** from [QuiverQuant.com](https://www.quiverquant.com/) + +2. **Add to Claude Desktop configuration**: + + **macOS**: `~/Library/Application Support/Claude/claude_desktop_config.json` + **Windows**: `%APPDATA%\Claude\claude_desktop_config.json` + + ```json + { + "mcpServers": { + "quiver": { + "command": "npx", + "args": ["-y", "github:usnavy13/quiverMCP"], + "env": { + "QUIVER_API_TOKEN": "your_token_here" + } + } + } + } + ``` + +3. **Restart Claude Desktop** - The QuiverAPI tools will be available in your conversations! + +#### Manual STDIO Usage + +```bash +# Install dependencies +npm install + +# Set your API token +export QUIVER_API_TOKEN=your_token_here + +# Run in development mode +npm run dev:stdio + +# Or build and run in production +npm run build +npm run start:stdio +``` + +#### Testing STDIO Mode + +```bash +# Run the built-in STDIO test suite +npm run test:stdio + +# Or test manually with a simple MCP message +echo '{"jsonrpc":"2.0","id":1,"method":"tools/list"}' | npm run start:stdio +``` + +### 2. **HTTP Mode**: For LibreChat and Web Integration + +For LibreChat and web-based MCP clients that require HTTP endpoints. -2. **Stdio Mode**: For Claude Desktop and CLI-based MCP clients - - Uses stdin/stdout communication - - Start with: `npm run start:stdio` +- **Endpoint**: `http://localhost:3000/message` +- **Start with**: `npm start` +- **Health check**: `http://localhost:3000/health` ## 📊 Monitoring diff --git a/package.json b/package.json index 820f20a..18dfb88 100644 --- a/package.json +++ b/package.json @@ -12,6 +12,7 @@ "dev:stdio": "tsx src/index.ts", "watch": "tsc --watch", "test": "tsx test-endpoints.ts", + "test:stdio": "node test-stdio.js", "test:comprehensive": "tsx tests/comprehensive-test-suite.ts", "test:config": "tsx tests/config-validation-suite.ts", "test:all": "npm run test:config http://localhost:3000 && npm run test:comprehensive http://localhost:3000" diff --git a/test-stdio.js b/test-stdio.js new file mode 100644 index 0000000..7e13bf3 --- /dev/null +++ b/test-stdio.js @@ -0,0 +1,164 @@ +#!/usr/bin/env node + +/** + * Simple test script to verify STDIO MCP functionality + * Usage: node test-stdio.js + */ + +import { spawn } from 'child_process'; +import { fileURLToPath } from 'url'; +import { dirname, join } from 'path'; + +const __filename = fileURLToPath(import.meta.url); +const __dirname = dirname(__filename); + +// Test cases +const testCases = [ + { + name: 'Initialize', + message: { + jsonrpc: '2.0', + id: 1, + method: 'initialize', + params: { + protocolVersion: '2024-11-05', + capabilities: {}, + clientInfo: { name: 'test-client', version: '1.0.0' } + } + } + }, + { + name: 'List Tools', + message: { + jsonrpc: '2.0', + id: 2, + method: 'tools/list' + } + }, + { + name: 'List Resources', + message: { + jsonrpc: '2.0', + id: 3, + method: 'resources/list' + } + }, + { + name: 'List Prompts', + message: { + jsonrpc: '2.0', + id: 4, + method: 'prompts/list' + } + } +]; + +async function runTest() { + console.log('🧪 Testing QuiverMCP STDIO Mode\n'); + + // Set test environment + const env = { + ...process.env, + QUIVER_API_TOKEN: 'test_token_for_stdio_test' + }; + + const serverPath = join(__dirname, 'build', 'index.js'); + + for (const testCase of testCases) { + console.log(`📋 Testing: ${testCase.name}`); + + try { + const result = await testStdioMessage(serverPath, testCase.message, env); + + if (result.success) { + console.log(`✅ ${testCase.name} - PASSED`); + if (testCase.name === 'List Tools' && result.data?.result?.tools) { + console.log(` 📊 Found ${result.data.result.tools.length} tools`); + } + } else { + console.log(`❌ ${testCase.name} - FAILED: ${result.error}`); + } + } catch (error) { + console.log(`❌ ${testCase.name} - ERROR: ${error.message}`); + } + + console.log(''); + } + + console.log('🏁 STDIO test completed'); +} + +function testStdioMessage(serverPath, message, env) { + return new Promise((resolve) => { + const child = spawn('node', [serverPath], { + env, + stdio: ['pipe', 'pipe', 'pipe'] + }); + + let stdout = ''; + let stderr = ''; + let resolved = false; + + // Set timeout + const timeout = setTimeout(() => { + if (!resolved) { + resolved = true; + child.kill(); + resolve({ success: false, error: 'Timeout' }); + } + }, 3000); + + child.stdout.on('data', (data) => { + stdout += data.toString(); + + // Try to parse JSON response + try { + const lines = stdout.split('\n').filter(line => line.trim()); + for (const line of lines) { + if (line.startsWith('{')) { + const response = JSON.parse(line); + if (response.id === message.id && !resolved) { + resolved = true; + clearTimeout(timeout); + child.kill(); + resolve({ success: true, data: response }); + return; + } + } + } + } catch (e) { + // Continue collecting data + } + }); + + child.stderr.on('data', (data) => { + stderr += data.toString(); + }); + + child.on('close', (code) => { + if (!resolved) { + resolved = true; + clearTimeout(timeout); + if (code === 0) { + resolve({ success: false, error: 'No response received' }); + } else { + resolve({ success: false, error: `Process exited with code ${code}` }); + } + } + }); + + child.on('error', (error) => { + if (!resolved) { + resolved = true; + clearTimeout(timeout); + resolve({ success: false, error: error.message }); + } + }); + + // Send the test message + child.stdin.write(JSON.stringify(message) + '\n'); + }); +} + +// Run the test +runTest().catch(console.error); \ No newline at end of file