🐺 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:
23
packages/sdk/typescript/package.json
Normal file
23
packages/sdk/typescript/package.json
Normal file
@@ -0,0 +1,23 @@
|
||||
{
|
||||
"name": "@mcp-server/module-sdk",
|
||||
"version": "0.1.0",
|
||||
"description": "TypeScript SDK for MCP Server modules",
|
||||
"main": "dist/index.js",
|
||||
"types": "dist/index.d.ts",
|
||||
"scripts": {
|
||||
"build": "tsc",
|
||||
"dev": "tsc --watch"
|
||||
},
|
||||
"dependencies": {
|
||||
"nats": "^2.19.0",
|
||||
"zod": "^3.22.4",
|
||||
"pino": "^8.16.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/node": "^20.10.5",
|
||||
"typescript": "^5.3.3"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@mcp-server/types": "workspace:*"
|
||||
}
|
||||
}
|
||||
161
packages/sdk/typescript/src/ModuleBase.ts
Normal file
161
packages/sdk/typescript/src/ModuleBase.ts
Normal file
@@ -0,0 +1,161 @@
|
||||
import { connect, NatsConnection, JSONCodec } from 'nats';
|
||||
import { z } from 'zod';
|
||||
import pino from 'pino';
|
||||
import { ToolRequest, ToolResponse, ToolDefinition } from './types';
|
||||
|
||||
export interface ModuleConfig {
|
||||
name: string;
|
||||
natsUrl?: string;
|
||||
token?: string;
|
||||
logLevel?: 'debug' | 'info' | 'warn' | 'error';
|
||||
}
|
||||
|
||||
export abstract class ModuleBase {
|
||||
protected connection?: NatsConnection;
|
||||
protected jsonCodec = JSONCodec();
|
||||
protected logger: pino.Logger;
|
||||
protected tools = new Map<string, ToolDefinition>();
|
||||
|
||||
constructor(protected config: ModuleConfig) {
|
||||
this.logger = pino({
|
||||
level: config.logLevel || 'info',
|
||||
name: config.name,
|
||||
});
|
||||
}
|
||||
|
||||
async start(): Promise<void> {
|
||||
try {
|
||||
// Connect to NATS
|
||||
await this.connectNats();
|
||||
|
||||
// Register tools
|
||||
await this.registerTools();
|
||||
|
||||
// Announce tools
|
||||
await this.announceTools();
|
||||
|
||||
// Setup handlers
|
||||
this.setupHandlers();
|
||||
|
||||
// Mark as ready
|
||||
await this.markReady();
|
||||
|
||||
this.logger.info('Module started successfully');
|
||||
} catch (error) {
|
||||
this.logger.error({ error }, 'Failed to start module');
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
async stop(): Promise<void> {
|
||||
if (this.connection) {
|
||||
await this.connection.drain();
|
||||
await this.connection.close();
|
||||
}
|
||||
this.logger.info('Module stopped');
|
||||
}
|
||||
|
||||
protected abstract registerTools(): Promise<void>;
|
||||
|
||||
protected addTool(tool: ToolDefinition): void {
|
||||
this.tools.set(tool.name, tool);
|
||||
this.logger.info({ tool: tool.name }, 'Tool registered');
|
||||
}
|
||||
|
||||
private async connectNats(): Promise<void> {
|
||||
const url = this.config.natsUrl || process.env.NATS_URL || 'nats://localhost:4222';
|
||||
const token = this.config.token || process.env.MODULE_TOKEN;
|
||||
|
||||
this.connection = await connect({
|
||||
servers: url,
|
||||
name: this.config.name,
|
||||
token,
|
||||
});
|
||||
|
||||
this.logger.info({ url }, 'Connected to NATS');
|
||||
}
|
||||
|
||||
private async announceTools(): Promise<void> {
|
||||
const tools = Array.from(this.tools.values()).map(tool => ({
|
||||
name: tool.name,
|
||||
description: tool.description,
|
||||
inputSchema: tool.inputSchema,
|
||||
module: this.config.name,
|
||||
permissions: [],
|
||||
}));
|
||||
|
||||
this.connection!.publish(
|
||||
'tools.discovery',
|
||||
this.jsonCodec.encode({
|
||||
module: this.config.name,
|
||||
tools,
|
||||
}),
|
||||
);
|
||||
|
||||
this.logger.info({ count: tools.length }, 'Tools announced');
|
||||
}
|
||||
|
||||
private setupHandlers(): void {
|
||||
for (const [name, tool] of this.tools) {
|
||||
const subject = `tools.${this.config.name}.${name}.execute`;
|
||||
|
||||
const sub = this.connection!.subscribe(subject);
|
||||
|
||||
(async () => {
|
||||
for await (const msg of sub) {
|
||||
try {
|
||||
const request = this.jsonCodec.decode(msg.data) as ToolRequest;
|
||||
const startTime = Date.now();
|
||||
|
||||
this.logger.debug({ request }, 'Executing tool');
|
||||
|
||||
try {
|
||||
const result = await tool.handler(request.params);
|
||||
|
||||
const response: ToolResponse = {
|
||||
id: request.id,
|
||||
status: 'success',
|
||||
data: result,
|
||||
duration: Date.now() - startTime,
|
||||
};
|
||||
|
||||
msg.respond(this.jsonCodec.encode(response));
|
||||
} catch (error) {
|
||||
const response: ToolResponse = {
|
||||
id: request.id,
|
||||
status: 'error',
|
||||
error: {
|
||||
code: 'EXECUTION_ERROR',
|
||||
message: error instanceof Error ? error.message : 'Unknown error',
|
||||
details: error,
|
||||
},
|
||||
duration: Date.now() - startTime,
|
||||
};
|
||||
|
||||
msg.respond(this.jsonCodec.encode(response));
|
||||
}
|
||||
} catch (error) {
|
||||
this.logger.error({ error }, 'Failed to process request');
|
||||
}
|
||||
}
|
||||
})().catch(err => this.logger.error({ error: err }, 'Subscription error'));
|
||||
}
|
||||
}
|
||||
|
||||
private async markReady(): Promise<void> {
|
||||
// Signal to parent process that module is ready
|
||||
if (process.send) {
|
||||
process.send({ type: 'ready' });
|
||||
}
|
||||
}
|
||||
|
||||
protected createToolHandler<T>(
|
||||
schema: z.ZodSchema<T>,
|
||||
handler: (params: T) => Promise<unknown>,
|
||||
): (params: unknown) => Promise<unknown> {
|
||||
return async (params: unknown) => {
|
||||
const validated = schema.parse(params);
|
||||
return handler(validated);
|
||||
};
|
||||
}
|
||||
}
|
||||
2
packages/sdk/typescript/src/index.ts
Normal file
2
packages/sdk/typescript/src/index.ts
Normal file
@@ -0,0 +1,2 @@
|
||||
export * from './ModuleBase';
|
||||
export * from './types';
|
||||
36
packages/sdk/typescript/src/types.ts
Normal file
36
packages/sdk/typescript/src/types.ts
Normal file
@@ -0,0 +1,36 @@
|
||||
import { z } from 'zod';
|
||||
|
||||
export const ToolRequestSchema = z.object({
|
||||
id: z.string(),
|
||||
tool: z.string(),
|
||||
method: z.enum(['execute', 'describe', 'validate']),
|
||||
params: z.unknown(),
|
||||
timeout: z.number(),
|
||||
metadata: z.object({
|
||||
user: z.string().optional(),
|
||||
session: z.string().optional(),
|
||||
timestamp: z.number(),
|
||||
}),
|
||||
});
|
||||
|
||||
export const ToolResponseSchema = z.object({
|
||||
id: z.string(),
|
||||
status: z.enum(['success', 'error']),
|
||||
data: z.unknown().optional(),
|
||||
error: z.object({
|
||||
code: z.string(),
|
||||
message: z.string(),
|
||||
details: z.unknown().optional(),
|
||||
}).optional(),
|
||||
duration: z.number(),
|
||||
});
|
||||
|
||||
export type ToolRequest = z.infer<typeof ToolRequestSchema>;
|
||||
export type ToolResponse = z.infer<typeof ToolResponseSchema>;
|
||||
|
||||
export interface ToolDefinition {
|
||||
name: string;
|
||||
description: string;
|
||||
inputSchema: Record<string, unknown>;
|
||||
handler: (params: unknown) => Promise<unknown>;
|
||||
}
|
||||
9
packages/sdk/typescript/tsconfig.json
Normal file
9
packages/sdk/typescript/tsconfig.json
Normal file
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"extends": "../../../tsconfig.json",
|
||||
"compilerOptions": {
|
||||
"outDir": "./dist",
|
||||
"rootDir": "./src"
|
||||
},
|
||||
"include": ["src/**/*"],
|
||||
"exclude": ["node_modules", "dist"]
|
||||
}
|
||||
Reference in New Issue
Block a user