🐺 Initial commit - Lupul Augmentat MCP Server
- MCP server cu stdio transport pentru performanță maximă
- Tool-uri pentru file operations, HTTP requests, system commands
- Suport NATS pentru comunicare inter-module
- Configurare nginx cu API key auth și SSL
- Arhitectură modulară și extensibilă
🤖 Generated with Claude Code
This commit is contained in:
443
docs/api-reference.md
Normal file
443
docs/api-reference.md
Normal file
@@ -0,0 +1,443 @@
|
||||
# MCP Server API Reference
|
||||
|
||||
## Tool Handler Base Class
|
||||
|
||||
All tools extend the `ToolHandler` abstract class which provides lifecycle management, validation, and error handling.
|
||||
|
||||
### Class Definition
|
||||
|
||||
```typescript
|
||||
abstract class ToolHandler<TInput = any, TOutput = any> {
|
||||
constructor(
|
||||
config: ToolConfig,
|
||||
inputSchema: z.ZodSchema<TInput>,
|
||||
outputSchema: z.ZodSchema<TOutput>
|
||||
);
|
||||
|
||||
// Main execution method
|
||||
async execute(input: unknown, context: ToolContext): Promise<TOutput>;
|
||||
|
||||
// Lifecycle hooks (override in subclass)
|
||||
protected async initialize(): Promise<void>;
|
||||
protected async validate(input: TInput): Promise<TInput>;
|
||||
protected abstract handle(input: TInput, context: ToolContext): Promise<TOutput>;
|
||||
protected async cleanup(): Promise<void>;
|
||||
protected async checkPermissions(context: ToolContext): Promise<void>;
|
||||
}
|
||||
```
|
||||
|
||||
### Tool Configuration
|
||||
|
||||
```typescript
|
||||
interface ToolConfig {
|
||||
name: string; // Unique tool identifier
|
||||
description: string; // Human-readable description
|
||||
timeout?: number; // Execution timeout in ms (default: 30000)
|
||||
permissions?: string[]; // Required permissions
|
||||
}
|
||||
```
|
||||
|
||||
### Tool Context
|
||||
|
||||
```typescript
|
||||
interface ToolContext {
|
||||
requestId: string; // Unique request identifier
|
||||
permissions: string[]; // Granted permissions
|
||||
userId?: string; // Optional user identifier
|
||||
metadata?: Record<string, any>; // Additional context
|
||||
}
|
||||
```
|
||||
|
||||
## Creating a Custom Tool
|
||||
|
||||
### Basic Example
|
||||
|
||||
```typescript
|
||||
import { z } from 'zod';
|
||||
import { ToolHandler, ToolContext } from '@mcp/tools';
|
||||
|
||||
// Define schemas
|
||||
const InputSchema = z.object({
|
||||
message: z.string(),
|
||||
uppercase: z.boolean().optional(),
|
||||
});
|
||||
|
||||
const OutputSchema = z.object({
|
||||
result: z.string(),
|
||||
length: z.number(),
|
||||
});
|
||||
|
||||
type Input = z.infer<typeof InputSchema>;
|
||||
type Output = z.infer<typeof OutputSchema>;
|
||||
|
||||
// Implement tool
|
||||
export class EchoTool extends ToolHandler<Input, Output> {
|
||||
constructor() {
|
||||
super(
|
||||
{
|
||||
name: 'echo',
|
||||
description: 'Echo a message with transformations',
|
||||
timeout: 5000,
|
||||
},
|
||||
InputSchema,
|
||||
OutputSchema
|
||||
);
|
||||
}
|
||||
|
||||
protected async handle(input: Input, context: ToolContext): Promise<Output> {
|
||||
let result = input.message;
|
||||
|
||||
if (input.uppercase) {
|
||||
result = result.toUpperCase();
|
||||
}
|
||||
|
||||
this.logger.info({ message: result }, 'Echoing message');
|
||||
|
||||
return {
|
||||
result,
|
||||
length: result.length,
|
||||
};
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Advanced Example with Permissions
|
||||
|
||||
```typescript
|
||||
export class DatabaseQueryTool extends ToolHandler<QueryInput, QueryOutput> {
|
||||
private db: Database;
|
||||
|
||||
constructor() {
|
||||
super(
|
||||
{
|
||||
name: 'db_query',
|
||||
description: 'Execute database queries',
|
||||
timeout: 60000,
|
||||
permissions: ['database:read', 'database:write'],
|
||||
},
|
||||
QueryInputSchema,
|
||||
QueryOutputSchema
|
||||
);
|
||||
}
|
||||
|
||||
protected async initialize(): Promise<void> {
|
||||
// Connect to database
|
||||
this.db = await Database.connect(process.env.DATABASE_URL);
|
||||
}
|
||||
|
||||
protected async checkPermissions(context: ToolContext): Promise<void> {
|
||||
const isWrite = this.isWriteQuery(this.currentInput.query);
|
||||
|
||||
if (isWrite && !context.permissions.includes('database:write')) {
|
||||
throw new Error('Permission denied: database:write required');
|
||||
}
|
||||
|
||||
if (!context.permissions.includes('database:read')) {
|
||||
throw new Error('Permission denied: database:read required');
|
||||
}
|
||||
}
|
||||
|
||||
protected async validate(input: QueryInput): Promise<QueryInput> {
|
||||
// Additional validation beyond schema
|
||||
if (this.containsSqlInjection(input.query)) {
|
||||
throw new Error('Invalid query: potential SQL injection');
|
||||
}
|
||||
|
||||
return input;
|
||||
}
|
||||
|
||||
protected async handle(input: QueryInput, context: ToolContext): Promise<QueryOutput> {
|
||||
const startTime = Date.now();
|
||||
|
||||
try {
|
||||
const results = await this.db.query(input.query, input.params);
|
||||
|
||||
return {
|
||||
rows: results.rows,
|
||||
rowCount: results.rowCount,
|
||||
duration: Date.now() - startTime,
|
||||
};
|
||||
} catch (error) {
|
||||
this.logger.error({ error, query: input.query }, 'Query failed');
|
||||
throw new Error(`Database query failed: ${error.message}`);
|
||||
}
|
||||
}
|
||||
|
||||
protected async cleanup(): Promise<void> {
|
||||
// Close database connection
|
||||
if (this.db) {
|
||||
await this.db.close();
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Tool Registration
|
||||
|
||||
### Registering Built-in Tools
|
||||
|
||||
```typescript
|
||||
import { ToolRegistry } from '@mcp/registry';
|
||||
import { FileReadTool, FileWriteTool } from './tools';
|
||||
|
||||
const registry = new ToolRegistry(natsClient);
|
||||
|
||||
// Built-in tools are registered automatically
|
||||
// But you can also register manually
|
||||
registry.registerBuiltinTool('file_read', new FileReadTool());
|
||||
registry.registerBuiltinTool('file_write', new FileWriteTool());
|
||||
```
|
||||
|
||||
### Registering External Module Tools
|
||||
|
||||
```typescript
|
||||
// External tools are discovered via NATS
|
||||
// Modules announce their tools on startup
|
||||
natsClient.publish('tools.discovery', {
|
||||
module: 'my-module',
|
||||
tools: [
|
||||
{
|
||||
name: 'my_tool',
|
||||
description: 'Custom tool from module',
|
||||
inputSchema: { /* ... */ },
|
||||
permissions: ['custom:permission'],
|
||||
}
|
||||
]
|
||||
});
|
||||
```
|
||||
|
||||
## Error Handling
|
||||
|
||||
### Error Types
|
||||
|
||||
```typescript
|
||||
// Base error class
|
||||
export class ToolError extends Error {
|
||||
constructor(
|
||||
message: string,
|
||||
public code: string,
|
||||
public details?: any
|
||||
) {
|
||||
super(message);
|
||||
this.name = 'ToolError';
|
||||
}
|
||||
}
|
||||
|
||||
// Specific error types
|
||||
export class ValidationError extends ToolError {
|
||||
constructor(message: string, details?: any) {
|
||||
super(message, 'VALIDATION_ERROR', details);
|
||||
}
|
||||
}
|
||||
|
||||
export class PermissionError extends ToolError {
|
||||
constructor(message: string, required: string[]) {
|
||||
super(message, 'PERMISSION_ERROR', { required });
|
||||
}
|
||||
}
|
||||
|
||||
export class TimeoutError extends ToolError {
|
||||
constructor(timeout: number) {
|
||||
super(`Operation timed out after ${timeout}ms`, 'TIMEOUT_ERROR');
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Error Handling in Tools
|
||||
|
||||
```typescript
|
||||
protected async handle(input: Input, context: ToolContext): Promise<Output> {
|
||||
try {
|
||||
// Tool logic
|
||||
} catch (error) {
|
||||
if (error.code === 'ENOENT') {
|
||||
throw new ToolError('File not found', 'FILE_NOT_FOUND', { path: input.path });
|
||||
}
|
||||
|
||||
// Re-throw unknown errors
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Testing Tools
|
||||
|
||||
### Unit Testing
|
||||
|
||||
```typescript
|
||||
import { MyTool } from './MyTool';
|
||||
import { ToolContext } from '@mcp/tools';
|
||||
|
||||
describe('MyTool', () => {
|
||||
let tool: MyTool;
|
||||
|
||||
beforeEach(() => {
|
||||
tool = new MyTool();
|
||||
});
|
||||
|
||||
const createContext = (permissions: string[] = []): ToolContext => ({
|
||||
requestId: 'test-request',
|
||||
permissions,
|
||||
});
|
||||
|
||||
it('should execute successfully', async () => {
|
||||
const result = await tool.execute(
|
||||
{ input: 'test' },
|
||||
createContext(['required:permission'])
|
||||
);
|
||||
|
||||
expect(result).toEqual({ output: 'expected' });
|
||||
});
|
||||
|
||||
it('should require permissions', async () => {
|
||||
await expect(
|
||||
tool.execute({ input: 'test' }, createContext())
|
||||
).rejects.toThrow('Permission denied');
|
||||
});
|
||||
});
|
||||
```
|
||||
|
||||
### Integration Testing
|
||||
|
||||
```typescript
|
||||
import { ToolRegistry } from '@mcp/registry';
|
||||
import { NatsClient } from '@mcp/nats';
|
||||
|
||||
describe('Tool Integration', () => {
|
||||
let registry: ToolRegistry;
|
||||
let nats: NatsClient;
|
||||
|
||||
beforeAll(async () => {
|
||||
nats = new NatsClient();
|
||||
await nats.connect();
|
||||
registry = new ToolRegistry(nats);
|
||||
});
|
||||
|
||||
afterAll(async () => {
|
||||
await nats.close();
|
||||
});
|
||||
|
||||
it('should execute tool via registry', async () => {
|
||||
const result = await registry.executeTool(
|
||||
'my_tool',
|
||||
{ input: 'test' }
|
||||
);
|
||||
|
||||
expect(result).toBeDefined();
|
||||
});
|
||||
});
|
||||
```
|
||||
|
||||
## Performance Considerations
|
||||
|
||||
### Timeout Management
|
||||
|
||||
```typescript
|
||||
protected async handle(input: Input, context: ToolContext): Promise<Output> {
|
||||
// Use AbortController for cancellable operations
|
||||
const controller = new AbortController();
|
||||
|
||||
const timeoutId = setTimeout(
|
||||
() => controller.abort(),
|
||||
this.config.timeout || 30000
|
||||
);
|
||||
|
||||
try {
|
||||
const result = await fetch(input.url, {
|
||||
signal: controller.signal,
|
||||
});
|
||||
|
||||
return processResult(result);
|
||||
} finally {
|
||||
clearTimeout(timeoutId);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Resource Management
|
||||
|
||||
```typescript
|
||||
export class ResourceIntensiveTool extends ToolHandler {
|
||||
private pool: ResourcePool;
|
||||
|
||||
protected async initialize(): Promise<void> {
|
||||
// Initialize resource pool
|
||||
this.pool = new ResourcePool({ max: 10 });
|
||||
}
|
||||
|
||||
protected async handle(input: Input, context: ToolContext): Promise<Output> {
|
||||
// Acquire resource from pool
|
||||
const resource = await this.pool.acquire();
|
||||
|
||||
try {
|
||||
return await this.processWithResource(resource, input);
|
||||
} finally {
|
||||
// Always release resource
|
||||
this.pool.release(resource);
|
||||
}
|
||||
}
|
||||
|
||||
protected async cleanup(): Promise<void> {
|
||||
// Drain pool on cleanup
|
||||
await this.pool.drain();
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Security Best Practices
|
||||
|
||||
1. **Always validate input** - Use Zod schemas and additional validation
|
||||
2. **Check permissions** - Implement checkPermissions for sensitive operations
|
||||
3. **Sanitize paths** - Prevent directory traversal attacks
|
||||
4. **Limit resource usage** - Implement timeouts and size limits
|
||||
5. **Log security events** - Track permission denials and suspicious activity
|
||||
6. **Use prepared statements** - Prevent SQL injection in database tools
|
||||
7. **Validate URLs** - Block internal/private IP ranges in HTTP tools
|
||||
|
||||
## Debugging Tools
|
||||
|
||||
### Logging
|
||||
|
||||
```typescript
|
||||
protected async handle(input: Input, context: ToolContext): Promise<Output> {
|
||||
this.logger.debug({ input }, 'Processing request');
|
||||
|
||||
try {
|
||||
const result = await this.process(input);
|
||||
this.logger.info({ result }, 'Request successful');
|
||||
return result;
|
||||
} catch (error) {
|
||||
this.logger.error({ error, input }, 'Request failed');
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Metrics
|
||||
|
||||
```typescript
|
||||
protected async handle(input: Input, context: ToolContext): Promise<Output> {
|
||||
const timer = this.metrics.startTimer('tool_execution_duration', {
|
||||
tool: this.config.name,
|
||||
});
|
||||
|
||||
try {
|
||||
const result = await this.process(input);
|
||||
|
||||
this.metrics.increment('tool_execution_success', {
|
||||
tool: this.config.name,
|
||||
});
|
||||
|
||||
return result;
|
||||
} catch (error) {
|
||||
this.metrics.increment('tool_execution_error', {
|
||||
tool: this.config.name,
|
||||
error: error.code,
|
||||
});
|
||||
|
||||
throw error;
|
||||
} finally {
|
||||
timer.end();
|
||||
}
|
||||
}
|
||||
```
|
||||
Reference in New Issue
Block a user