Skip to main content
Glama
index.ts11.5 kB
#!/usr/bin/env node import { Server } from '@modelcontextprotocol/sdk/server/index.js'; import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js'; import { CallToolRequestSchema, ListToolsRequestSchema, ListResourcesRequestSchema, ReadResourceRequestSchema, } from '@modelcontextprotocol/sdk/types.js'; const DOCS_BASE_URL = 'https://docs.statly.live'; const API_BASE_URL = 'https://docs.statly.live/api'; // Documentation content (embedded for offline access) const SDK_REFERENCE = { javascript: { installation: 'npm install @statly/observe', init: `import { init } from '@statly/observe'; init({ dsn: 'https://sk_live_xxx@statly.live/your-org' });`, captureException: `import { captureException } from '@statly/observe'; try { riskyOperation(); } catch (e) { captureException(e); }`, functions: [ 'init(options) - Initialize SDK', 'captureException(error, context?) - Capture error', 'captureMessage(message, level?) - Capture message', 'setUser(user) - Set user context', 'setTag(key, value) - Add tag', 'setTags(tags) - Add multiple tags', 'addBreadcrumb(crumb) - Add breadcrumb', 'flush() - Wait for pending events', 'close() - Shutdown SDK', ], integrations: ['Express', 'Next.js', 'Fastify'], }, python: { installation: 'pip install statly-observe', init: `import statly statly.init(dsn='https://sk_live_xxx@statly.live/your-org')`, captureException: `import statly try: risky_operation() except Exception as e: statly.capture_exception(e)`, functions: [ 'init(dsn, **kwargs) - Initialize SDK', 'capture_exception(exception?, context?) - Capture error', 'capture_message(message, level?, context?) - Capture message', 'set_user(id?, email?, username?) - Set user context', 'set_tag(key, value) - Add tag', 'set_tags(tags) - Add multiple tags', 'add_breadcrumb(message, **kwargs) - Add breadcrumb', 'flush(timeout?) - Wait for pending events', 'close(timeout?) - Shutdown SDK', ], integrations: ['Flask', 'Django', 'FastAPI'], }, go: { installation: 'go get github.com/statly/statly-observe-go', init: `import statly "github.com/statly/statly-observe-go" statly.Init(statly.Options{DSN: "https://sk_live_xxx@statly.live/your-org"}) defer statly.Close()`, captureException: `statly.CaptureException(err)`, functions: [ 'Init(options) error - Initialize SDK', 'CaptureException(err) string - Capture error', 'CaptureMessage(message, level) string - Capture message', 'SetUser(user) - Set user context', 'SetTag(key, value) - Add tag', 'SetTags(tags) - Add multiple tags', 'AddBreadcrumb(crumb) - Add breadcrumb', 'Recover() - Panic recovery (use with defer)', 'Flush() - Wait for pending events', 'Close() - Shutdown SDK', ], integrations: ['Gin', 'Echo'], }, }; const API_REFERENCE = { authentication: 'Authorization: Bearer sk_live_xxx', endpoints: [ 'GET /api/v1/monitors - List monitors', 'POST /api/v1/monitors - Create monitor', 'GET /api/v1/monitors/{id} - Get monitor', 'PATCH /api/v1/monitors/{id} - Update monitor', 'DELETE /api/v1/monitors/{id} - Delete monitor', 'GET /api/v1/incidents - List incidents', 'POST /api/v1/incidents - Create incident', 'GET /api/v1/incidents/{id} - Get incident', 'PATCH /api/v1/incidents/{id} - Update incident', 'GET /api/v1/status/{slug} - Get public status (no auth)', ], rateLimits: { free: '50 req/min', hobby: '100 req/min', pro: '300 req/min', enterprise: '1000 req/min', }, }; const server = new Server( { name: 'statly-docs-mcp', version: '0.1.0', }, { capabilities: { tools: {}, resources: {}, }, } ); // List available tools server.setRequestHandler(ListToolsRequestSchema, async () => ({ tools: [ { name: 'search_docs', description: 'Search Statly documentation for a topic', inputSchema: { type: 'object', properties: { query: { type: 'string', description: 'Search query (e.g., "express integration", "capture error")', }, }, required: ['query'], }, }, { name: 'get_sdk_reference', description: 'Get SDK API reference for a specific language', inputSchema: { type: 'object', properties: { language: { type: 'string', enum: ['javascript', 'python', 'go'], description: 'Programming language', }, }, required: ['language'], }, }, { name: 'get_code_example', description: 'Get a code example for a specific use case', inputSchema: { type: 'object', properties: { language: { type: 'string', enum: ['javascript', 'python', 'go'], description: 'Programming language', }, topic: { type: 'string', enum: ['installation', 'init', 'capture_error', 'user_context', 'breadcrumbs'], description: 'Topic for the code example', }, }, required: ['language', 'topic'], }, }, { name: 'get_api_reference', description: 'Get REST API reference information', inputSchema: { type: 'object', properties: {}, }, }, ], })); // Handle tool calls server.setRequestHandler(CallToolRequestSchema, async (request) => { const { name, arguments: args } = request.params; switch (name) { case 'search_docs': { const query = (args as { query: string }).query.toLowerCase(); const results: string[] = []; // Search SDK docs for (const [lang, ref] of Object.entries(SDK_REFERENCE)) { if (query.includes(lang) || query.includes('sdk')) { results.push(`## ${lang.toUpperCase()} SDK\nInstall: ${ref.installation}\nIntegrations: ${ref.integrations.join(', ')}`); } if (query.includes('install')) { results.push(`${lang}: ${ref.installation}`); } } // Search API docs if (query.includes('api') || query.includes('endpoint') || query.includes('rest')) { results.push(`## API Reference\n${API_REFERENCE.endpoints.join('\n')}`); } if (query.includes('auth')) { results.push(`## Authentication\n${API_REFERENCE.authentication}`); } if (query.includes('rate') || query.includes('limit')) { results.push(`## Rate Limits\n${JSON.stringify(API_REFERENCE.rateLimits, null, 2)}`); } return { content: [ { type: 'text', text: results.length > 0 ? results.join('\n\n') : `No results for "${query}". Try: sdk, api, install, auth, rate limits`, }, ], }; } case 'get_sdk_reference': { const lang = (args as { language: string }).language; const ref = SDK_REFERENCE[lang as keyof typeof SDK_REFERENCE]; if (!ref) { return { content: [{ type: 'text', text: `Unknown language: ${lang}` }], }; } return { content: [ { type: 'text', text: `# ${lang.toUpperCase()} SDK Reference ## Installation ${ref.installation} ## Quick Start ${ref.init} ## Functions ${ref.functions.map(f => `- ${f}`).join('\n')} ## Framework Integrations ${ref.integrations.join(', ')} For full docs: ${DOCS_BASE_URL}/docs/sdk/${lang}`, }, ], }; } case 'get_code_example': { const { language, topic } = args as { language: string; topic: string }; const ref = SDK_REFERENCE[language as keyof typeof SDK_REFERENCE]; if (!ref) { return { content: [{ type: 'text', text: `Unknown language: ${language}` }], }; } let code = ''; switch (topic) { case 'installation': code = ref.installation; break; case 'init': code = ref.init; break; case 'capture_error': code = ref.captureException; break; case 'user_context': if (language === 'javascript') { code = `import { setUser } from '@statly/observe'; setUser({ id: 'user_123', email: 'user@example.com' });`; } else if (language === 'python') { code = `import statly statly.set_user(id='user_123', email='user@example.com')`; } else { code = `statly.SetUser(statly.User{ID: "user_123", Email: "user@example.com"})`; } break; case 'breadcrumbs': if (language === 'javascript') { code = `import { addBreadcrumb } from '@statly/observe'; addBreadcrumb({ category: 'navigation', message: 'User clicked checkout', level: 'info' });`; } else if (language === 'python') { code = `import statly statly.add_breadcrumb(message='User clicked checkout', category='navigation', level='info')`; } else { code = `statly.AddBreadcrumb(statly.Breadcrumb{ Message: "User clicked checkout", Category: "navigation", Level: statly.LevelInfo, })`; } break; } return { content: [ { type: 'text', text: `\`\`\`${language}\n${code}\n\`\`\``, }, ], }; } case 'get_api_reference': { return { content: [ { type: 'text', text: `# Statly REST API Reference ## Authentication ${API_REFERENCE.authentication} ## Endpoints ${API_REFERENCE.endpoints.map(e => `- ${e}`).join('\n')} ## Rate Limits - Free: ${API_REFERENCE.rateLimits.free} - Hobby: ${API_REFERENCE.rateLimits.hobby} - Pro: ${API_REFERENCE.rateLimits.pro} - Enterprise: ${API_REFERENCE.rateLimits.enterprise} Full docs: ${DOCS_BASE_URL}/docs/api`, }, ], }; } default: return { content: [{ type: 'text', text: `Unknown tool: ${name}` }], }; } }); // List resources server.setRequestHandler(ListResourcesRequestSchema, async () => ({ resources: [ { uri: 'statly://docs/llms.txt', name: 'Statly LLMs Documentation', description: 'Complete documentation for AI agents in llms.txt format', mimeType: 'text/plain', }, ], })); // Read resources server.setRequestHandler(ReadResourceRequestSchema, async (request) => { const { uri } = request.params; if (uri === 'statly://docs/llms.txt') { // Fetch the llms.txt content try { const response = await fetch(`${DOCS_BASE_URL}/llms.txt`); const text = await response.text(); return { contents: [ { uri, mimeType: 'text/plain', text, }, ], }; } catch { return { contents: [ { uri, mimeType: 'text/plain', text: 'Failed to fetch llms.txt. Visit https://docs.statly.live/llms.txt', }, ], }; } } return { contents: [], }; }); // Start server async function main() { const transport = new StdioServerTransport(); await server.connect(transport); console.error('Statly Docs MCP server running'); } main().catch(console.error);

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/KodyDennon/statly-docs-mcp'

If you have feedback or need assistance with the MCP directory API, please join our Discord server