#!/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);