OsirisOsiris

Osiris SDK

Comprehensive framework for building authenticated, stateful, and policy-driven MCPs

The Osiris SDK is a comprehensive framework for building authenticated, stateful, and policy-driven MCPs (Model Context Protocol) with enterprise-grade security. It handles authentication, state management, and policy enforcement automatically, letting you focus on building intelligent MCP tools.

Installation

The fastest way to get started is using the Osiris CLI:

# Install CLI
npm install -g @osiris-ai/cli

# Register account and get credentials
npx @osiris-ai/cli register

# Create OAuth Client
npx @osiris-ai/cli create-client

# Connect authentication services
npx @osiris-ai/cli connect-auth

# Create your project
npx @osiris-ai/cli create-app

This creates a complete project with:

  • Authentication setup
  • Database configuration
  • Example tools and resources
  • Environment configuration
  • TypeScript/JavaScript templates

Option 2: Manual Installation

npm install @osiris-ai/sdk

Quick Start

CLI-Generated Project

When you run osiris create-app, you get a complete project structure:

your-project/
├── index.ts              # Main MCP server entry point
├── client.ts             # MCP client setup and configuration
├── tools/
│   └── hello-world.ts    # Example tool
├── resources/
│   └── hello-world.ts    # Example resource
├── prompts/
│   └── hello-world.ts    # Example prompt
├── schema/
│   └── index.ts          # Zod schema definitions
├── .env                  # Environment variables (auto-configured)
├── .env.example          # Environment template
├── osiris.json           # Project configuration
└── package.json          # Dependencies and scripts

Generated Environment Configuration

The CLI automatically configures your .env file:

# Osiris Hub Configuration
HUB_BASE_URL=https://api.osirislabs.xyz/v1

# OAuth Credentials (auto-filled by CLI)
OAUTH_CLIENT_ID=your-oauth-client-id
OAUTH_CLIENT_SECRET=your-oauth-client-secret

# MCP Server Configuration
PORT=3000
NODE_ENV=development

# Database Configuration
DATABASE_ADAPTER=postgres
DATABASE_URL=postgresql://username:password@localhost:5432/your-project

Generated Server Code

Here's what the CLI generates in index.ts:

import { createMcpServer } from '@osiris-ai/sdk';
import { PostgresDatabaseAdapter } from '@osiris-ai/sdk';
import { configureClient } from './client.js';
import dotenv from 'dotenv';

dotenv.config();

const databaseAdapter = new PostgresDatabaseAdapter({
    connectionString: process.env.DATABASE_URL!
});

await createMcpServer({
    name: 'your-project',
    version: '1.0.0',
    
    auth: {
        useHub: true,
        hubConfig: {
            baseUrl: process.env.HUB_BASE_URL!,
            clientId: process.env.OAUTH_CLIENT_ID!,
            clientSecret: process.env.OAUTH_CLIENT_SECRET!,
        },
        database: databaseAdapter,
    },
    
    server: {
        port: parseInt(process.env.PORT || '3000'),
        mcpPath: '/mcp',
        callbackBasePath: '/callback',
    },

    configure: configureClient
});

Generated Client Configuration

The client.ts file registers your tools, resources, and prompts:

import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
import { registerHelloWorldTool } from './tools/hello-world.js';
import { registerHelloWorldResource } from './resources/hello-world.js';
import { registerHelloWorldPrompt } from './prompts/hello-world.js';

export function configureClient(server: McpServer) {
    // Register tools
    registerHelloWorldTool(server);
    
    // Register resources
    registerHelloWorldResource(server);
    
    // Register prompts
    registerHelloWorldPrompt(server);
}

Authentication Architecture

Osiris SDK provides dual-layer authentication that secures both user access and LLM interactions, with two distinct approaches: Hub Authentication and Local Authentication.

Hub vs Local Authentication

Hub Authentication (Centralized)

Best for: Production MCPs, marketplace distribution, simplified setup

await createMcpServer({
    name: 'my-mcp',
    version: '1.0.0',
    auth: {
        useHub: true,
        hubConfig: {
            baseUrl: 'https://hub.osiris.dev/v1',
            clientId: process.env.OAUTH_CLIENT_ID!,
            clientSecret: process.env.OAUTH_CLIENT_SECRET!,
        },
        database: new PostgresDatabaseAdapter({
            connectionString: process.env.DATABASE_URL!
        })
    }
});

How it works:

  1. User authenticates once with Osiris Hub
  2. Hub manages all OAuth tokens centrally
  3. MCPs receive tokens via hub's proxy system
  4. All API calls route through the hub
  5. Hub handles token refresh automatically

Benefits:

  • Single Sign-On: One login for all MCPs
  • Zero Token Management: Hub handles everything
  • Enterprise Security: Secrets never touch your server
  • Automatic Refresh: No token expiry issues
  • Marketplace Ready: Built for distribution

Local Authentication (Direct)

Best for: Internal tools, custom flows, maximum control

import { GoogleAuthenticator } from '@osiris-ai/google-sdk';
import { GitHubAuthenticator } from '@osiris-ai/github-sdk';
import { PostgresSecretSharingAuthenticator } from '@osiris-ai/postgres-sdk';

await createMcpServer({
    name: 'my-mcp',
    version: '1.0.0',
    auth: {
        useHub: false,
        directAuth: {
            google: new GoogleAuthenticator({
                clientId: process.env.GOOGLE_CLIENT_ID!,
                clientSecret: process.env.GOOGLE_CLIENT_SECRET!,
                redirectUri: 'http://localhost:3000/google/callback',
                allowedScopes: ['gmail.readonly', 'calendar.readonly']
            }),
            github: new GitHubAuthenticator({
                clientId: process.env.GITHUB_CLIENT_ID!,
                clientSecret: process.env.GITHUB_CLIENT_SECRET!,
                redirectUri: 'http://localhost:3000/github/callback',
                allowedScopes: ['repo', 'user']
            })
        },
        database: new PostgresDatabaseAdapter({
            connectionString: process.env.DATABASE_URL!
        })
    }
});

How it works:

  1. Users authenticate directly with each service
  2. MCP server manages tokens locally
  3. Direct API calls to service providers
  4. Local database stores credentials

Benefits:

  • Full Control: Custom authentication logic
  • No Dependencies: Independent of external services
  • Flexible Storage: Choose your credential storage
  • Custom Flows: Build unique authentication experiences
  • Enterprise Compliance: Meet specific security requirements

Layer 1: Service Authentication Types

OAuth Authentication

Best for: API integrations with Google, GitHub, Discord, etc.

// Hub-based OAuth (recommended)
const context = getAuthContext();
const googleToken = context.getToken('google');
const gmail = createHubGmailClient({ hubBaseUrl: process.env.HUB_BASE_URL! });

// Local OAuth (direct control)
const context = getAuthContext();
const googleToken = context.getToken('google');
const gmail = new GoogleClient({ accessToken: googleToken.access_token });

Security Model: OAuth scopes define MCP access permissions

  • ✅ Industry-standard OAuth 2.0 flows
  • ✅ User controls permissions via scopes
  • ✅ Automatic or manual token refresh

Secret Sharing (Direct Access)

Best for: Database connections, API keys, internal services

// Hub-based secrets
const context = getAuthContext();
const dbCredentials = context.getTokens('osiris');

// Local secrets  
// Currently not supported

Security Model: Direct credential management

  • ✅ Encrypted storage and transmission
  • ✅ User-controlled access and sharing
  • ✅ Custom validation and connection testing

Embedded Wallets (Policy Engine Security)

Best for: Blockchain interactions, financial operations

// Policy-enforced wallet interactions
const { token, context } = getAuthContext("osiris");
const client = new EVMWalletClient(
    hubBaseUrl, 
    token.access_token, 
    context.deploymentId, 
    chainId
);
const walletRecords = await client.getWalletRecords();
const account = await client.getViemAccount(walletRecords[0].metadata.wallet.addresses[0]);

Security Model: Policy engine enforced at wallet level

  • User sets policies in their wallet
  • MCP/LLM cannot execute invalid transactions
  • Hardware-backed wallet security
  • Multi-signature support

Layer 2: LLM ↔ MCP Authentication

Prevents unauthorized LLM access to your MCP:

When a user creates a new deployment, the Hub generates a unique deployment ID that can be used for MCP authentication. With the upcoming MCP Router feature, users won't need to deploy individual MCPs - they can use a single MCP to securely call other MCPs. This reduces context load on LLMs while maintaining secure authentication.

The Osiris SDK provides clear error messages that LLMs can interpret to guide users through the MCP authentication process. This enables:

  • ✅ Automated deployment ID generation
  • ✅ Simplified MCP routing and authentication
  • ✅ Reduced LLM context requirements
  • ✅ Clear authentication guidance

Local Authentication APIs

When using local authentication (useHub: false), the SDK automatically exposes REST APIs to help users authenticate and manage connections. These APIs enable building custom authentication UIs and workflows.

Session Management

Create Authentication Session

Start a new authentication session for connecting services:

POST /deployments/sessions
Content-Type: application/json

{
    "packageId": "my-gmail-mcp",
    "name": "Gmail Integration", 
    "description": "Access Gmail for email management"
}

Response:

{
    "status": "success",
    "sessionId": "550e8400-e29b-41d4-a716-446655440000",
    "availableServices": [
        {
            "name": "google",
            "type": "oauth", 
            "displayName": "Google",
            "description": "Google OAuth authentication",
            "required": false
        },
        {
            "name": "postgres",
            "type": "secret",
            "displayName": "PostgreSQL", 
            "description": "Database connection",
            "required": false
        }
    ],
    "expiresAt": "2024-01-15T10:30:00Z"
}

Get Session Status

Check authentication progress and connected services:

GET /deployments/sessions/{sessionId}

Response:

{
    "status": "success",
    "data": {
        "sessionId": "550e8400-e29b-41d4-a716-446655440000",
        "name": "Gmail Integration",
        "packageId": "my-gmail-mcp",
        "connectedServices": [
            {
                "service": "google",
                "status": "connected",
                "userId": "user@gmail.com",
                "connectedAt": "2024-01-15T10:25:00Z"
            }
        ],
        "readyToCreate": true,
        "expiresAt": "2024-01-15T10:30:00Z"
    }
}

OAuth Authentication Flow

Get Authorization URL

Generate OAuth authorization URL for a service:

GET /deployments/sessions/{sessionId}/auth/{service}/authorize

Response:

{
    "status": "success",
    "data": {
        "authUrl": "https://accounts.google.com/oauth/authorize?client_id=...",
        "state": "550e8400-e29b-41d4-a716-446655440000"
    }
}

OAuth Callback

The SDK automatically handles OAuth callbacks:

GET /{service}/callback?code={auth_code}&state={session_id}

Users are redirected here after OAuth consent. The SDK processes the callback automatically.

Secret-Based Authentication

Connect with Credentials

For database connections and API keys:

POST /deployments/sessions/{sessionId}/auth/{service}/connect
Content-Type: application/json

{
    "host": "localhost",
    "port": 5432,
    "database": "myapp", 
    "username": "appuser",
    "password": "secretpass"
}

Response:

{
    "status": "success",
    "data": {
        "session_id": "550e8400-e29b-41d4-a716-446655440000",
        "service": "postgres",
        "connectionTest": "passed"
    }
}

Deployment Creation

Finalize Deployment

Create permanent deployment with all connected services:

POST /deployments/sessions/{sessionId}/create

Response:

{
    "status": "success", 
    "deploymentId": "deployment-123",
    "connectedServices": ["google", "postgres"],
    "mcpEndpoint": "http://localhost:3000/mcp"
}

Action Execution

Execute MCP Actions

Perform authenticated actions on behalf of users:

POST /deployments/{deploymentId}/mcp/{service}/action
Content-Type: application/json

{
    "method": "GET",
    "url": "/gmail/v1/users/me/messages",
    "data": {},
    "service": "google"
}

Building Authentication UIs

Use these APIs to build custom authentication interfaces:

// React example for OAuth flow
const startOAuthFlow = async (sessionId: string, service: string) => {
    const response = await fetch(
        `/deployments/sessions/${sessionId}/auth/${service}/authorize`
    );
    const { data } = await response.json();
    
    // Redirect user to OAuth provider
    window.location.href = data.authUrl;
};

// Check authentication status
const checkAuthStatus = async (sessionId: string) => {
    const response = await fetch(`/deployments/sessions/${sessionId}`);
    const { data } = await response.json();
    
    return {
        connectedServices: data.connectedServices,
        readyToCreate: data.readyToCreate
    };
};

Manual MCP Server Setup

If you prefer to set up manually instead of using the CLI:

Basic MCP Server

import { createMcpServer, PostgresDatabaseAdapter } from '@osiris-ai/sdk';

await createMcpServer({
    name: 'my-mcp',
    version: '1.0.0',
    
    auth: {
        useHub: true,
        hubConfig: {
            baseUrl: process.env.HUB_BASE_URL!,
            clientId: process.env.OAUTH_CLIENT_ID!,
            clientSecret: process.env.OAUTH_CLIENT_SECRET!,
        },
        database: new PostgresDatabaseAdapter({
            connectionString: process.env.DATABASE_URL!
        }),
    },
    
    server: {
        port: 3001,
        mcpPath: '/mcp',
        callbackBasePath: '/callback',
    },

    configure: (server) => {
        server.tool('example_tool', 'Example tool', {
            message: { type: 'string', description: 'Message to process' }
        }, async ({ message }) => {
            return {
                content: [{ type: 'text', text: `Processed: ${message}` }]
            };
        });
    }
});

Gmail MCP Example

import { createMcpServer, getAuthContext, createHubGmailClient } from '@osiris-ai/sdk';

class GmailMCP {
    private hubBaseUrl: string;

    constructor(hubBaseUrl: string) {
        this.hubBaseUrl = hubBaseUrl;
    }

    getGmailClient() {
        const context = getAuthContext();
        
        if (!context.tokens) {
            throw new Error('No Google token available. Please authenticate with Google first.');
        }

        return createHubGmailClient({
            hubBaseUrl: this.hubBaseUrl
        });
    }

    configureServer(server) {
        server.tool(
            'send_email',
            'Send an email through Gmail',
            {
                to: { type: 'string', description: 'Recipient email' },
                subject: { type: 'string', description: 'Email subject' },
                body: { type: 'string', description: 'Email content' }
            },
            async ({ to, subject, body }) => {
                const gmail = this.getGmailClient();
                
                const emailContent = [
                    `To: ${to}`,
                    `Subject: ${subject}`,
                    'MIME-Version: 1.0',
                    'Content-Type: text/plain; charset=UTF-8',
                    '',
                    body
                ];
                
                const raw = Buffer.from(emailContent.join('\r\n'))
                    .toString('base64')
                    .replace(/\+/g, '-')
                    .replace(/\//g, '_');
                
                const result = await gmail.users.messages.send({
                    userId: 'me',
                    requestBody: { raw }
                });
                
                return {
                    content: [{
                        type: 'text',
                        text: `✅ Email sent successfully! Message ID: ${result.data.id}`
                    }]
                };
            }
        );
    }
}

// Start server
const gmailMcp = new GmailMCP(process.env.HUB_BASE_URL!);

await createMcpServer({
    name: 'gmail-mcp',
    version: '1.0.0',
    
    auth: {
        useHub: true,
        hubConfig: {
            baseUrl: process.env.HUB_BASE_URL!,
            clientId: process.env.OAUTH_CLIENT_ID!,
            clientSecret: process.env.OAUTH_CLIENT_SECRET!,
        },
        database: new PostgresDatabaseAdapter({
            connectionString: process.env.DATABASE_URL!
        }),
    },
    
    server: {
        port: 3001,
        mcpPath: '/mcp',
        callbackBasePath: '/callback',
    },

    configure: (server) => {
        gmailMcp.configureServer(server);
    }
});

Stateful MCP Architecture

Osiris MCPs maintain persistent state across conversations:

State Storage Options

const { token, context } = getAuthContext("osiris");
const response = await fetch(`https://api.osirislabs.xyz/v1/hub/action/${deploymentId}/postgres`, {
    method: "POST",
    body: JSON.stringify({
        sql: `SELECT * FROM user_data WHERE user_id = $1`,
        params: [context.userId]
    })
});

Option 2: MCP's Database

database: new PostgresDatabaseAdapter({
    connectionString: process.env.MCP_DATABASE_URL!,
})

Database Adapters

Development

import { MemoryDatabaseAdapter } from '@osiris-ai/sdk';

database: new MemoryDatabaseAdapter() // Data stored in memory (lost on restart)

Production

import { PostgresDatabaseAdapter } from '@osiris-ai/sdk';

database: new PostgresDatabaseAdapter({
    connectionString: process.env.DATABASE_URL!
})

Other Options

import { MongoDBDatabaseAdapter, RedisDatabaseAdapter, SQLiteDatabaseAdapter } from '@osiris-ai/sdk';

// MongoDB
database: new MongoDBDatabaseAdapter({
    connectionString: 'mongodb://localhost:27017/osiris'
})

// Redis  
database: new RedisDatabaseAdapter({
    host: 'localhost',
    port: 6379
})

// SQLite
database: new SQLiteDatabaseAdapter({
    filepath: './mcp_data.db'
})

Token Context System

The Osiris SDK provides request-scoped authentication context:

Basic Context Usage

import { getAuthContext } from '@osiris-ai/sdk';

server.tool('context_example', 'Shows how to use auth context', schema, async (params) => {
  // For OAuth services
  const context = getAuthContext();
  if (!context.tokens) {
    throw new Error('No tokens available. Please authenticate first.');
  }
  
  // For embedded wallets  
  const { token, context: walletContext } = getAuthContext("osiris");
  if (!token || !walletContext) {
    throw new Error("No wallet token or context found");
  }
  
  return { content: [{ type: 'text', text: 'Context accessed successfully' }] };
});

Multi-Tenant Isolation

Each deployment automatically isolates user data:

// Context is automatically scoped to current user
const context = getAuthContext();
console.log('User ID:', context.userId);
console.log('Deployment ID:', context.deploymentId);

// All database operations are automatically user-scoped
const userEmails = await fetchUserEmails(context.userId);

Error Handling

Authentication Errors

import { AuthContextError } from '@osiris-ai/sdk';

server.tool('robust_tool', 'Tool with error handling', schema, async (params) => {
    try {
        const gmail = createHubGmailClient({ hubBaseUrl: process.env.HUB_BASE_URL! });
        const result = await gmail.users.messages.list({ userId: 'me' });
        
        return {
            content: [{ type: 'text', text: 'Success!' }]
        };
        
    } catch (error) {
        if (error instanceof AuthContextError) {
            return {
                content: [{
                    type: 'text',
                    text: `🔐 Authentication required: ${error.authorizationUrl}`
                }],
                isError: true
            };
        }
        
        return {
            content: [{
                type: 'text', 
                text: `❌ Error: ${error.message}`
            }],
            isError: true
        };
    }
});

Wallet Transaction Errors

server.tool('wallet_tool', 'Wallet operation', schema, async (params) => {
    try {
        const { token, context } = getAuthContext("osiris");
        if (!token || !context) {
            throw new Error("Wallet authentication required");
        }
        
        // Wallet operation - policy engine validates automatically
        const result = await performWalletOperation();
        
        return {
            content: [{ type: 'text', text: `✅ Transaction: ${result.hash}` }]
        };
        
    } catch (error) {
        if (error.message.includes('policy')) {
            return {
                content: [{
                    type: 'text',
                    text: `🚫 Transaction blocked by wallet policy: ${error.message}`
                }],
                isError: true
            };
        }
        
        return {
            content: [{
                type: 'text',
                text: `💳 Wallet error: ${error.message}`
            }],
            isError: true
        };
    }
});

Development Workflow

1. Start Development Server

# From your CLI-generated project
npm run dev

# Or for TypeScript projects
npm run dev  # Uses tsx for hot reload

2. Testing Your MCP

The generated project includes example tools you can test immediately:

# Test the hello world tool
curl -X POST http://localhost:3000/mcp \
  -H "Content-Type: application/json" \
  -d '{"method": "tools/call", "params": {"name": "hello_world", "arguments": {"name": "Alice"}}}'

3. Adding New Tools

Create a new tool in the tools/ directory:

// tools/my-new-tool.ts
import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
import { z } from 'zod';

export function registerMyNewTool(server: McpServer) {
    const MyToolSchema = z.object({
        param1: z.string().describe('Parameter description')
    });
    
    server.tool('my_new_tool', 'Description of what this tool does', MyToolSchema, async ({ param1 }) => {
        // Your tool logic here
        return {
            content: [{
                type: 'text',
                text: `Tool result: ${param1}`
            }]
        };
    });
}

Then register it in client.ts:

import { registerMyNewTool } from './tools/my-new-tool.js';

export function configureClient(server: McpServer) {
    registerHelloWorldTool(server);
    registerMyNewTool(server); // Add this line
    // ... other registrations
}

Environment Variables

# Core Configuration
HUB_BASE_URL=https://api.osirislabs.xyz/v1
PORT=3000

# OAuth Authentication (auto-configured by CLI)
OAUTH_CLIENT_ID=your-oauth-client-id
OAUTH_CLIENT_SECRET=your-oauth-client-secret

# Database
DATABASE_URL=postgresql://user:pass@localhost:5432/mcp_db

# Deployment Security
MCP_DEPLOYMENT_SECRET=your-deployment-secret

MCP Development Best Practices

1. Choose the Right Authentication Approach

Decision Framework: Hub for marketplace MCPs, Local for enterprise/custom solutions

Hub Authentication

Choose when:

  • Building for marketplace distribution
  • Want simplified setup and maintenance
  • Need automatic token management
  • Targeting non-technical users
// ✅ GOOD: Hub authentication for production MCPs
auth: {
    useHub: true,
    hubConfig: {
        baseUrl: 'https://hub.osiris.dev/v1',
        clientId: process.env.OAUTH_CLIENT_ID!,
        clientSecret: process.env.OAUTH_CLIENT_SECRET!,
    }
}

Local Authentication

Choose when:

  • Building internal/enterprise tools
  • Need custom authentication flows
  • Have specific compliance requirements
  • Want maximum control over user data
// ✅ GOOD: Local authentication for enterprise MCPs
auth: {
    useHub: false,
    directAuth: {
        google: new GoogleAuthenticator({...}),
        postgres: new PostgresAuthenticator({...})
    }
}

2. Use the CLI for Initial Setup

✅ RECOMMENDED: Always start with CLI-generated projects

# Get properly configured authentication
osiris register
osiris connect-auth
osiris create-app

❌ AVOID: Manual credential management from scratch

# Don't manually configure OAuth without understanding security implications

3. Handle Authentication Context Properly

Basic Context Validation

// ✅ GOOD: Always validate auth context
const context = getAuthContext();
if (!context.tokens) {
    throw new Error('Authentication required. Please connect your accounts first.');
}

// ❌ BAD: Assuming context exists
const context = getAuthContext();
const googleToken = context.getToken('google'); // May throw

Service-Specific Authentication

// ✅ GOOD: Handle service-specific auth
server.tool('gmail_tool', 'Access Gmail', schema, async (params) => {
    const context = getAuthContext();
    const googleToken = context.getToken('google');
    
    if (!googleToken) {
        return {
            content: [{
                type: 'text',
                text: '🔐 Please connect your Google account to use Gmail features.'
            }],
            isError: true
        };
    }
    
    // Use authenticated client
    const gmail = createHubGmailClient({ hubBaseUrl: process.env.HUB_BASE_URL! });
    // ... rest of implementation
});

Wallet Context for Blockchain Operations

// ✅ GOOD: Handle wallet context for DeFi/crypto operations
server.tool('swap_tokens', 'Swap tokens on Uniswap', schema, async (params) => {
    const { token, context } = getAuthContext("osiris");
    
    if (!token || !context) {
        return {
            content: [{
                type: 'text',
                text: '🔐 Please connect your wallet to perform token swaps.'
            }],
            isError: true
        };
    }
    
    // Policy-enforced wallet operations
    const client = new EVMWalletClient(hubBaseUrl, token.access_token, context.deploymentId, chainId);
    // ... rest of implementation
});

4. Implement Intelligent Stateful Patterns

User Learning and Personalization

// ✅ GOOD: Learn from user behavior
server.tool('smart_email_assistant', 'AI email assistant', schema, async (params) => {
    const context = getAuthContext();
    
    // Load user preferences and history
    const userProfile = await getUserProfile(context.userId);
    const emailHistory = await getEmailHistory(context.userId);
    
    // Make intelligent decisions based on data
    const recommendation = await generateEmailRecommendation({
        userProfile,
        emailHistory,
        currentRequest: params
    });
    
    // Store interaction for future learning
    await storeUserInteraction(context.userId, {
        tool: 'smart_email_assistant',
        input: params,
        output: recommendation,
        timestamp: new Date(),
        feedback: null // Will be updated if user provides feedback
    });
    
    return recommendation;
});

Cross-Session State Management

// ✅ GOOD: Maintain state across MCP sessions
class StatefulMCP {
    private userSessions = new Map<string, UserSession>();
    
    async initializeUserSession(userId: string) {
        if (!this.userSessions.has(userId)) {
            const session = await loadUserSession(userId);
            this.userSessions.set(userId, session);
        }
        return this.userSessions.get(userId);
    }
    
    configureServer(server: McpServer) {
        server.tool('contextual_tool', 'Context-aware tool', schema, async (params) => {
            const context = getAuthContext();
            const session = await this.initializeUserSession(context.userId);
            
            // Use accumulated context for better responses
            const result = await processWithContext(params, session.context);
            
            // Update session state
            session.context.push({ input: params, output: result, timestamp: new Date() });
            await saveUserSession(context.userId, session);
            
            return result;
        });
    }
}

5. Error Handling and User Experience

Actionable Error Messages

// ✅ GOOD: Provide specific, actionable error messages
catch (error) {
    if (error instanceof AuthContextError) {
        return {
            content: [{
                type: 'text',
                text: `🔐 Authentication required. Please visit: ${error.authorizationUrl}`
            }],
            isError: true
        };
    }
    
    if (error.code === 'INSUFFICIENT_PERMISSIONS') {
        return {
            content: [{
                type: 'text',
                text: `❌ Insufficient permissions. Please grant additional scopes: ${error.requiredScopes.join(', ')}`
            }],
            isError: true
        };
    }
    
    // Log detailed error for debugging
    console.error('Tool execution error:', {
        error: error.message,
        stack: error.stack,
        userId: context.userId,
        tool: 'tool_name'
    });
    
    return {
        content: [{
            type: 'text',
            text: '❌ An error occurred. Please try again or contact support.'
        }],
        isError: true
    };
}

// ❌ BAD: Generic, unhelpful errors
catch (error) {
    return { content: [{ type: 'text', text: 'Error' }], isError: true };
}

Graceful Degradation

// ✅ GOOD: Provide alternative functionality when possible
server.tool('smart_calendar', 'Calendar management', schema, async (params) => {
    const context = getAuthContext();
    const googleToken = context.getToken('google');
    
    if (!googleToken) {
        // Fallback to read-only functionality or suggestions
        return {
            content: [{
                type: 'text',
                text: '📅 I can help you plan your calendar, but to create events I need access to your Google Calendar. Please connect your Google account for full functionality.'
            }]
        };
    }
    
    // Full functionality with authentication
    const calendar = createHubCalendarClient({ hubBaseUrl: process.env.HUB_BASE_URL! });
    // ... rest of implementation
});

6. Security Best Practices

Scope Validation

// ✅ GOOD: Validate required scopes before operations
const validateGoogleScopes = (requiredScopes: string[]) => {
    const context = getAuthContext();
    const googleToken = context.getToken('google');
    
    if (!googleToken) return false;
    
    const userScopes = googleToken.scope?.split(' ') || [];
    return requiredScopes.every(scope => userScopes.includes(scope));
};

server.tool('delete_emails', 'Delete emails', schema, async (params) => {
    if (!validateGoogleScopes(['gmail.modify'])) {
        return {
            content: [{
                type: 'text',
                text: '❌ This operation requires additional permissions. Please reconnect with modify access.'
            }],
            isError: true
        };
    }
    
    // Proceed with delete operation
});

Input Validation and Sanitization

// ✅ GOOD: Validate and sanitize all inputs
import { z } from 'zod';

const emailSchema = z.object({
    to: z.string().email(),
    subject: z.string().max(998), // RFC 5322 limit
    body: z.string().max(10000), // Reasonable limit
    priority: z.enum(['low', 'normal', 'high']).optional()
});

server.tool('send_email', 'Send email', emailSchema, async (params) => {
    // Zod validates automatically, but add business logic validation
    if (params.body.includes('<script>')) {
        throw new Error('Script tags are not allowed in email content');
    }
    
    // Safe to proceed with validated data
});

7. Performance and Scalability

Efficient Token Management

// ✅ GOOD: Cache frequently accessed data
class PerformantMCP {
    private tokenCache = new Map<string, { token: any, expires: Date }>();
    
    async getValidToken(service: string) {
        const cacheKey = `${service}-${context.userId}`;
        const cached = this.tokenCache.get(cacheKey);
        
        if (cached && cached.expires > new Date()) {
            return cached.token;
        }
        
        const context = getAuthContext();
        const token = context.getToken(service);
        
        // Cache with expiry buffer
        const expires = new Date(Date.now() + (token.expires_in - 300) * 1000);
        this.tokenCache.set(cacheKey, { token, expires });
        
        return token;
    }
}

8. Testing and Development

Mock Authentication for Testing

// ✅ GOOD: Create mock authentication for tests
if (process.env.NODE_ENV === 'test') {
    // Override getAuthContext for testing
    const mockContext = {
        userId: 'test-user',
        deploymentId: 'test-deployment',
        tokens: new Map([
            ['google', { access_token: 'mock-token', expires_in: 3600 }]
        ]),
        getToken: (service: string) => mockContext.tokens.get(service)
    };
    
    // Use mock context in tests
}

9. Documentation and Maintenance

Document Authentication Requirements

// ✅ GOOD: Clear documentation for each tool
server.tool(
    'gmail_search',
    'Search Gmail messages (requires Gmail read access)',
    {
        query: { 
            type: 'string', 
            description: 'Search query (Gmail search syntax)' 
        }
    },
    async (params) => {
        // Implementation with clear authentication requirements
    }
);

Version Compatibility

// ✅ GOOD: Handle API version changes gracefully
const handleApiCall = async (endpoint: string, data: any) => {
    try {
        return await apiCall(endpoint, data);
    } catch (error) {
        if (error.status === 410) { // API deprecated
            // Fall back to newer API or provide migration guidance
            return await handleApiMigration(endpoint, data);
        }
        throw error;
    }
};

Next Steps

On this page

InstallationOption 1: Using CLI (Recommended)Option 2: Manual InstallationQuick StartCLI-Generated ProjectGenerated Environment ConfigurationGenerated Server CodeGenerated Client ConfigurationAuthentication ArchitectureHub vs Local AuthenticationHub Authentication (Centralized)Local Authentication (Direct)Layer 1: Service Authentication TypesOAuth AuthenticationSecret Sharing (Direct Access)Embedded Wallets (Policy Engine Security)Layer 2: LLM ↔ MCP AuthenticationLocal Authentication APIsSession ManagementCreate Authentication SessionGet Session StatusOAuth Authentication FlowGet Authorization URLOAuth CallbackSecret-Based AuthenticationConnect with CredentialsDeployment CreationFinalize DeploymentAction ExecutionExecute MCP ActionsBuilding Authentication UIsManual MCP Server SetupBasic MCP ServerGmail MCP ExampleStateful MCP ArchitectureState Storage OptionsOption 1: User's Database (Recommended)Option 2: MCP's DatabaseDatabase AdaptersDevelopmentProductionOther OptionsToken Context SystemBasic Context UsageMulti-Tenant IsolationError HandlingAuthentication ErrorsWallet Transaction ErrorsDevelopment Workflow1. Start Development Server2. Testing Your MCP3. Adding New ToolsEnvironment VariablesMCP Development Best Practices1. Choose the Right Authentication ApproachHub AuthenticationLocal Authentication2. Use the CLI for Initial Setup3. Handle Authentication Context ProperlyBasic Context ValidationService-Specific AuthenticationWallet Context for Blockchain Operations4. Implement Intelligent Stateful PatternsUser Learning and PersonalizationCross-Session State Management5. Error Handling and User ExperienceActionable Error MessagesGraceful Degradation6. Security Best PracticesScope ValidationInput Validation and Sanitization7. Performance and ScalabilityEfficient Token Management8. Testing and DevelopmentMock Authentication for Testing9. Documentation and MaintenanceDocument Authentication RequirementsVersion CompatibilityNext Steps