Claude Desktop Integration¶
This guide covers integration of AIRS MCP servers with Claude Desktop.
Overview¶
AIRS MCP provides Claude Desktop integration through MCP protocol compliance. The integration supports both STDIO and HTTP transport methods for connecting to Claude Desktop applications.
Integration Methods¶
STDIO Integration¶
The most common integration method uses STDIO transport for local server communication:
- Build your MCP server
- Configure Claude Desktop
- Test the connection
Configuration Setup¶
Claude Desktop requires configuration in its settings file. The typical location varies by platform:
- macOS:
~/Library/Application Support/Claude/claude_desktop_config.json - Windows:
%APPDATA%\Claude\claude_desktop_config.json - Linux:
~/.config/Claude/claude_desktop_config.json
Example Configuration¶
{
"mcpServers": {
"airsprotocols-mcp-server": {
"command": "/path/to/your/mcp-server",
"args": [],
"env": {}
}
}
}
Server Implementation¶
Basic MCP server for Claude Desktop:
use airsprotocols_mcp::integration::server::McpServer;
use airsprotocols_mcp::transport::adapters::stdio::StdioTransport;
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
let server = McpServer::new()
.with_resource_provider(your_resource_provider)
.with_tool_provider(your_tool_provider);
let transport = StdioTransportClientBuilder::new();
server.serve(transport).await?;
Ok(())
}
Testing Integration¶
Basic Connection Test¶
- Start your MCP server manually to verify it works
- Check Claude Desktop logs for connection status
- Verify MCP operations work as expected
Debugging Connection Issues¶
Common issues and solutions:
- Server not starting: Check binary path and permissions
- Connection timeout: Verify server responds to initialization
- Protocol errors: Ensure MCP compliance and proper message handling
Example Integration¶
A complete working example is available in the examples directory:
# Navigate to example
cd crates/airsprotocols-mcp/examples/simple-mcp-server
# Build the server
cargo build --release
# Configure path in Claude Desktop
# Point to: target/release/simple-mcp-server
HTTP Integration¶
For remote server integration, use HTTP transport:
{
"mcpServers": {
"remote-server": {
"command": "npx",
"args": ["-y", "@modelcontextprotocol/server-http-client", "http://your-server:3000/mcp"]
}
}
}
Verification¶
After configuration:
- Restart Claude Desktop
- Check for your server in available tools
- Test basic operations like listing resources or tools
- Verify tool calls work correctly
Troubleshooting¶
Common Issues¶
- Server not visible: Check configuration file syntax and paths
- Permission errors: Ensure executable permissions on server binary
- Protocol errors: Verify server implements required MCP methods
- Timeout issues: Check server startup time and responsiveness
**Each script includes:** - User confirmation for sensitive operations - Comprehensive error handling with recovery instructions - Real-time status feedback and progress indicators - Automatic validation and verification steps ## Claude Desktop Configuration ### Configuration File Location Claude Desktop reads MCP servers from: - **macOS**: `~/Library/Application Support/Claude/claude_desktop_config.json` - **Windows**: `%APPDATA%\Claude\claude_desktop_config.json` - **Linux**: `~/.config/Claude/claude_desktop_config.json` ### Example Configuration ```json { "mcpServers": { "simple-mcp-server": { "command": "/path/to/your/simple-mcp-server", "args": [], "env": { "RUST_LOG": "info" } } } }
Advanced Configuration¶
{
"mcpServers": {
"airsprotocols-mcp-server": {
"command": "/usr/local/bin/airsprotocols-mcp-server",
"args": ["--config", "/etc/airsprotocols-mcp/config.json"],
"env": {
"RUST_LOG": "debug",
"MCP_SERVER_NAME": "AIRS Production Server",
"MAX_CONNECTIONS": "10",
"REQUEST_TIMEOUT": "30"
}
}
}
}
MCP Capabilities Integration¶
Tools Integration¶
Tools appear in Claude Desktop's MCP Tools interface and can be executed in real-time:
use airsprotocols_mcp::integration::mcp::{ToolProvider, McpError};
use airsprotocols_mcp::protocol::protocol::Tool;
use async_trait::async_trait;
use serde_json::{json, Value};
#[derive(Debug)]
struct CalculatorTool;
#[async_trait]
impl ToolProvider for CalculatorTool {
async fn list_tools(&self) -> Result<Vec<Tool>, McpError> {
Ok(vec![
Tool {
name: "add".to_string(),
description: "Add two numbers".to_string(),
input_schema: json!({
"type": "object",
"properties": {
"a": {"type": "number", "description": "First number"},
"b": {"type": "number", "description": "Second number"}
},
"required": ["a", "b"]
}),
}
])
}
async fn call_tool(&self, name: &str, args: Value) -> Result<Vec<Content>, McpError> {
match name {
"add" => {
let a = args["a"].as_f64().unwrap_or(0.0);
let b = args["b"].as_f64().unwrap_or(0.0);
let result = a + b;
Ok(vec![Content::Text {
text: format!("Result: {} + {} = {}", a, b, result),
}])
}
_ => Err(McpError::InvalidRequest {
message: format!("Unknown tool: {}", name),
})
}
}
}
Resources Integration¶
Resources are accessible through Claude Desktop's attachment menu:
use airsprotocols_mcp::integration::mcp::{ResourceProvider, McpError};
use airsprotocols_mcp::protocol::protocol::{Resource, Content, Uri, MimeType};
use async_trait::async_trait;
#[derive(Debug)]
struct FileSystemProvider {
base_path: String,
}
#[async_trait]
impl ResourceProvider for FileSystemProvider {
async fn list_resources(&self) -> Result<Vec<Resource>, McpError> {
Ok(vec![
Resource {
uri: Uri::new("file:///docs/readme.txt")?,
name: "Project README".to_string(),
description: Some("Main project documentation".to_string()),
mime_type: Some(MimeType::new("text/plain")?),
},
Resource {
uri: Uri::new("file:///config/app.json")?,
name: "App Configuration".to_string(),
description: Some("Application configuration file".to_string()),
mime_type: Some(MimeType::new("application/json")?),
},
])
}
async fn read_resource(&self, uri: &str) -> Result<Vec<Content>, McpError> {
match uri {
"file:///docs/readme.txt" => {
Ok(vec![Content::Text {
text: std::fs::read_to_string(&format!("{}/docs/readme.txt", self.base_path))
.map_err(|e| McpError::InvalidRequest {
message: format!("Failed to read file: {}", e),
})?,
}])
}
"file:///config/app.json" => {
let content = std::fs::read_to_string(&format!("{}/config/app.json", self.base_path))
.map_err(|e| McpError::InvalidRequest {
message: format!("Failed to read file: {}", e),
})?;
Ok(vec![Content::Text { text: content }])
}
_ => Err(McpError::InvalidRequest {
message: format!("Resource not found: {}", uri),
})
}
}
}
Prompts Integration¶
Prompts appear in Claude Desktop's prompt template interface:
use airsprotocols_mcp::integration::mcp::{PromptProvider, McpError};
use airsprotocols_mcp::protocol::protocol::{Prompt, PromptArgument, PromptMessage};
use async_trait::async_trait;
use serde_json::Value;
#[derive(Debug)]
struct CodeReviewPrompts;
#[async_trait]
impl PromptProvider for CodeReviewPrompts {
async fn list_prompts(&self) -> Result<Vec<Prompt>, McpError> {
Ok(vec![
Prompt {
name: "code-review".to_string(),
description: "Perform comprehensive code review".to_string(),
arguments: vec![
PromptArgument {
name: "language".to_string(),
description: "Programming language".to_string(),
required: true,
},
PromptArgument {
name: "code".to_string(),
description: "Code to review".to_string(),
required: true,
}
],
}
])
}
async fn get_prompt(&self, name: &str, args: Value) -> Result<PromptMessage, McpError> {
match name {
"code-review" => {
let language = args["language"].as_str().unwrap_or("unknown");
let code = args["code"].as_str().unwrap_or("");
Ok(PromptMessage {
role: "user".to_string(),
content: format!(
"Please review this {} code for:\n\
• Code quality and best practices\n\
• Potential bugs or issues\n\
• Performance optimizations\n\
• Security considerations\n\n\
```{}\n{}\n```",
language, language, code
),
})
}
_ => Err(McpError::InvalidRequest {
message: format!("Unknown prompt: {}", name),
})
}
}
}
Server Implementation¶
Complete MCP Server¶
use airsprotocols_mcp::integration::mcp::McpServerBuilder;
use airsprotocols_mcp::protocol::protocol::ServerCapabilities;
use tracing::info;
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
// Initialize logging (file-based to avoid STDIO contamination)
init_logging()?;
info!("Starting AIRS MCP Server for Claude Desktop");
// Create server capabilities
let capabilities = ServerCapabilities::default()
.with_tools()
.with_resources()
.with_prompts()
.with_logging();
// Build server with providers
let server = McpServerBuilder::new()
.capabilities(capabilities)
.tool_provider(Box::new(CalculatorTool))
.resource_provider(Box::new(FileSystemProvider {
base_path: "/tmp".to_string(),
}))
.prompt_provider(Box::new(CodeReviewPrompts))
.build()?;
info!("Server built successfully, starting JSON-RPC communication");
// Run server (connects to Claude Desktop via STDIO)
server.run().await?;
info!("🔄 Server shutdown complete");
Ok(())
}
fn init_logging() -> Result<(), Box<dyn std::error::Error>> {
use tracing_subscriber::{fmt, EnvFilter};
use tracing_appender::rolling::{RollingFileAppender, Rotation};
let file_appender = RollingFileAppender::new(
Rotation::DAILY,
"/tmp/airsprotocols-mcp-server",
"server.log",
);
fmt()
.with_writer(file_appender)
.with_env_filter(EnvFilter::new("info"))
.init();
Ok(())
}
Testing and Validation¶
MCP Inspector Testing¶
Test your server with the browser-based MCP Inspector:
# Run MCP Inspector tests
./scripts/test_inspector.sh
# Manual testing
npx @modelcontextprotocol/inspector /path/to/your/server
Claude Desktop Verification¶
- Tools Testing: Look for your tools in the MCP icon in Claude's chat interface
- Resources Testing: Check the attachment menu for "Add from [your-server-name]"
- Prompts Testing: Find your prompts in the prompt template interface
Real-time Monitoring¶
# Monitor server logs
tail -f /tmp/airsprotocols-mcp-server/server.log
# Debug integration status
./scripts/debug_integration.sh
# Watch Claude Desktop logs (macOS)
tail -f ~/Library/Logs/Claude/claude-desktop.log
Production Deployment¶
Security Considerations¶
// Secure file system access
impl FileSystemProvider {
fn validate_path(&self, path: &str) -> Result<(), McpError> {
let canonical = std::fs::canonicalize(path)
.map_err(|_| McpError::InvalidRequest {
message: "Invalid file path".to_string(),
})?;
if !canonical.starts_with(&self.base_path) {
return Err(McpError::InvalidRequest {
message: "Path outside allowed directory".to_string(),
});
}
Ok(())
}
}
Performance Optimization¶
use airsprotocols_mcp::protocol::jsonrpc::concurrent::ConcurrentJsonRpcConfig;
use std::time::Duration;
let config = ConcurrentJsonRpcConfig::builder()
.request_timeout(Duration::from_secs(30))
.max_concurrent_requests(50)
.buffer_size(16384)
.enable_correlation_tracking(true)
.build();
let server = McpServerBuilder::new()
.with_config(config)
.build()?;
Error Recovery¶
use tokio::signal;
async fn run_with_graceful_shutdown() -> Result<(), Box<dyn std::error::Error>> {
let server = build_server()?;
let shutdown = async {
signal::ctrl_c().await.expect("Failed to listen for Ctrl+C");
info!("Received shutdown signal");
};
tokio::select! {
result = server.run() => {
match result {
Ok(_) => info!("Server completed normally"),
Err(e) => {
error!("Server error: {}", e);
return Err(e.into());
}
}
}
_ = shutdown => {
info!("Shutting down gracefully");
}
}
Ok(())
}
Troubleshooting¶
Common Issues¶
- Server Not Appearing in Claude: Check configuration file path and syntax
- Permission Denied: Ensure server binary has execute permissions
- Connection Refused: Verify server starts without errors
- Tools Not Working: Check tool schema validation
Debug Commands¶
# Validate configuration
jq empty claude_desktop_config.json
# Test server directly
echo '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{}}' | your-server
# Check file permissions
ls -la /path/to/your/server
# Monitor Claude Desktop process
ps aux | grep -i claude
Next: Advanced Patterns
Check back soon for comprehensive Claude Desktop integration guidance.