import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import axios, { AxiosRequestConfig } from "axios";
import { z } from "zod";
// Глобальное хранилище сессии для API ключей
class SessionManager {
private static instance: SessionManager;
private sessions: Map<string, string> = new Map(); // sessionId -> apiKey
private currentSession: string | null = null;
static getInstance(): SessionManager {
if (!SessionManager.instance) {
SessionManager.instance = new SessionManager();
}
return SessionManager.instance;
}
setApiKey(sessionId: string, apiKey: string): void {
this.sessions.set(sessionId, apiKey);
this.currentSession = sessionId;
console.log(`[AUTH] Session ${sessionId} created with API key`);
}
getApiKey(sessionId?: string): string | null {
const targetSession = sessionId || this.currentSession;
if (!targetSession) return null;
const apiKey = this.sessions.get(targetSession);
if (apiKey) {
console.log(`[AUTH] Using stored API key for session ${targetSession}`);
}
return apiKey || null;
}
getCurrentApiKey(): string | null {
return this.currentSession ? this.getApiKey(this.currentSession) : null;
}
getCurrentSession(): string | null {
return this.currentSession;
}
clearSession(sessionId?: string): void {
const targetSession = sessionId || this.currentSession;
if (targetSession) {
this.sessions.delete(targetSession);
if (this.currentSession === targetSession) {
this.currentSession = null;
}
console.log(`[AUTH] Session ${targetSession} cleared`);
}
}
clearAllSessions(): void {
this.sessions.clear();
this.currentSession = null;
console.log(`[AUTH] All sessions cleared`);
}
listSessions(): string[] {
return Array.from(this.sessions.keys());
}
}
// Helper function for authorization API requests
async function makeAuthRequest<T>(
method: string,
path: string,
body: any = null,
params: any = null,
apiKey?: string,
sessionId?: string
): Promise<T> {
let hostUrl = process.env.AUTH_API_HOST_URL || "https://api.development.myradius.ru";
if (!hostUrl.endsWith("/")) hostUrl += "/";
// Add platform-authorization prefix if not present
if (!hostUrl.includes("/platform-authorization")) {
hostUrl = hostUrl.replace(/\/$/, "") + "/platform-authorization/";
}
const url = `${hostUrl}${path}`;
// Получаем API ключ из параметров, сессии или переменной окружения
const sessionManager = SessionManager.getInstance();
const finalApiKey =
apiKey ||
sessionManager.getApiKey(sessionId) ||
sessionManager.getCurrentApiKey() ||
process.env.AUTH_API_KEY ||
"";
const headers: Record<string, string> = {
authorization: finalApiKey,
Accept: "application/json",
};
if (method.toUpperCase() !== "GET") {
headers["Content-Type"] = "application/json";
}
console.log("[DEBUG] Auth API Request", {
method,
url,
headers: { ...headers, authorization: "***" },
body,
params,
hasStoredApiKey: !!sessionManager.getCurrentApiKey(),
sessionId: sessionId || "current",
});
try {
const config: AxiosRequestConfig = {
url,
method,
headers,
params,
};
if (method.toUpperCase() !== "GET" && body !== null) {
config.data = body;
}
const response = await axios(config);
return response.data;
} catch (error) {
if (axios.isAxiosError(error)) {
if (error.response) {
console.error("[DEBUG] Auth API Error Response", {
status: error.response.status,
data: error.response.data,
});
}
throw new Error(`Auth API request failed: ${error.message}`);
}
throw error;
}
}
// Schema definitions for authorization
const AuthCheckPasswordSchema = z.object({
password: z.string().describe("Current password to check"),
api_key: z.string().optional().describe("API key for authentication (optional if already authenticated)"),
session_id: z.string().optional().describe("Session ID to use specific session (optional)"),
});
const AuthChangePasswordSchema = z.object({
password: z.string().describe("Current password"),
new_password: z.string().describe("New password"),
api_key: z.string().optional().describe("API key for authentication (optional if already authenticated)"),
session_id: z.string().optional().describe("Session ID to use specific session (optional)"),
});
const AuthLoginSchema = z.object({
api_key: z.string().describe("API key for authentication"),
session_id: z.string().optional().describe("Custom session ID (optional, will generate if not provided)"),
});
const AuthSessionSchema = z.object({
session_id: z.string().optional().describe("Session ID to manage (optional, uses current if not provided)"),
});
const AuthSignInSchema = z.object({
account: z.string().describe("Email account for login"),
password: z.string().describe("Password for login"),
platform: z.string().default("WEB").describe("Platform type (default: WEB)"),
session_id: z.string().optional().describe("Custom session ID (optional, will generate if not provided)"),
});
export const registerAuthTools = (server: McpServer) => {
const sessionManager = SessionManager.getInstance();
// Login/Set API key for session
server.tool(
"auth_login",
AuthLoginSchema.shape,
{
title: "Authenticate and store API key for session",
},
async (args) => {
const validatedArgs = AuthLoginSchema.parse(args);
const { api_key, session_id } = validatedArgs;
// Generate session ID if not provided
const sessionId = session_id || `session_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;
// Store API key in session
sessionManager.setApiKey(sessionId, api_key);
return {
content: [
{
type: "text",
text: JSON.stringify(
{
success: true,
message: "Authentication successful",
session_id: sessionId,
note: "API key stored for future requests",
},
null,
2
),
},
],
};
}
);
// Sign in and automatically store API key
server.tool(
"auth_sign_in",
AuthSignInSchema.shape,
{
title: "Sign in with email and password, automatically store API key",
},
async (args) => {
const validatedArgs = AuthSignInSchema.parse(args);
const { account, password, platform, session_id } = validatedArgs;
// Generate session ID if not provided
const sessionId = session_id || `session_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;
const requestBody = {
account,
password,
platform,
};
try {
const result: any = await makeAuthRequest(
"POST",
"api/v1/authorization/sign-in/",
requestBody,
null,
"", // No API key needed for sign-in
undefined
);
// Extract API key from response
const apiKey = result?.payload?.api_key;
if (!apiKey) {
throw new Error("API key not found in response");
}
// Store API key in session
sessionManager.setApiKey(sessionId, apiKey);
// Return user info and success message
const userInfo = {
user_id: result.payload.id,
first_name: result.payload.first_name,
last_name: result.payload.last_name,
account: result.payload.accounts?.[0]?.account,
company: result.payload.company?.name,
api_key: apiKey.substring(0, 8) + "...", // Show only first 8 chars for security
};
return {
content: [
{
type: "text",
text: JSON.stringify(
{
success: true,
message: "Sign in successful",
session_id: sessionId,
user_info: userInfo,
note: "API key automatically stored for future requests",
applications_count: result.payload.applications?.length || 0,
},
null,
2
),
},
],
};
} catch (error) {
return {
content: [
{
type: "text",
text: JSON.stringify(
{
success: false,
message: "Sign in failed",
error: error instanceof Error ? error.message : String(error),
session_id: sessionId,
},
null,
2
),
},
],
};
}
}
);
// Check current password
server.tool(
"auth_check_password",
AuthCheckPasswordSchema.shape,
{
title: "Check current password",
},
async (args) => {
const validatedArgs = AuthCheckPasswordSchema.parse(args);
const { password, api_key, session_id } = validatedArgs;
const requestBody = { password };
const result = await makeAuthRequest(
"POST",
"api/v1/authorization/check-my-password/",
requestBody,
null,
api_key,
session_id
);
return {
content: [
{
type: "text",
text: JSON.stringify(
{
success: true,
message: "Password check completed",
result: result || "Password verification successful",
},
null,
2
),
},
],
};
}
);
// Change password
server.tool(
"auth_change_password",
AuthChangePasswordSchema.shape,
{
title: "Change current password",
},
async (args) => {
const validatedArgs = AuthChangePasswordSchema.parse(args);
const { password, new_password, api_key, session_id } = validatedArgs;
const requestBody = {
password,
new_password,
};
const result = await makeAuthRequest(
"POST",
"api/v1/authorization/change-my-password/",
requestBody,
null,
api_key,
session_id
);
return {
content: [
{
type: "text",
text: JSON.stringify(
{
success: true,
message: "Password changed successfully",
result: result || "Password update completed",
},
null,
2
),
},
],
};
}
);
// Session management tools
server.tool(
"auth_session_info",
AuthSessionSchema.shape,
{
title: "Get current session information",
},
async (args) => {
const validatedArgs = AuthSessionSchema.parse(args);
const { session_id } = validatedArgs;
const currentSession = sessionManager.getCurrentSession();
const hasApiKey = sessionManager.getApiKey(session_id) !== null;
const allSessions = sessionManager.listSessions();
return {
content: [
{
type: "text",
text: JSON.stringify(
{
current_session: currentSession,
requested_session: session_id || currentSession,
has_api_key: hasApiKey,
total_sessions: allSessions.length,
all_sessions: allSessions,
},
null,
2
),
},
],
};
}
);
server.tool(
"auth_logout",
AuthSessionSchema.shape,
{
title: "Logout and clear session",
},
async (args) => {
const validatedArgs = AuthSessionSchema.parse(args);
const { session_id } = validatedArgs;
const targetSession = session_id || sessionManager.getCurrentSession();
sessionManager.clearSession(session_id);
return {
content: [
{
type: "text",
text: JSON.stringify(
{
success: true,
message: `Session ${targetSession} cleared successfully`,
remaining_sessions: sessionManager.listSessions(),
},
null,
2
),
},
],
};
}
);
// Test authenticated request (для проверки что авторизация работает)
server.tool(
"auth_test_connection",
AuthSessionSchema.shape,
{
title: "Test authenticated connection to authorization API",
},
async (args) => {
const validatedArgs = AuthSessionSchema.parse(args);
const { session_id } = validatedArgs;
try {
// Попробуем сделать простой запрос для проверки авторизации
const result = await makeAuthRequest("GET", "api/v1/", null, null, undefined, session_id);
return {
content: [
{
type: "text",
text: JSON.stringify(
{
success: true,
message: "Connection test successful",
session_info: {
session_id: session_id || sessionManager.getCurrentSession(),
has_stored_key: sessionManager.getCurrentApiKey() !== null,
},
result,
},
null,
2
),
},
],
};
} catch (error) {
return {
content: [
{
type: "text",
text: JSON.stringify(
{
success: false,
message: "Connection test failed",
error: error instanceof Error ? error.message : String(error),
session_info: {
session_id: session_id || sessionManager.getCurrentSession(),
has_stored_key: sessionManager.getCurrentApiKey() !== null,
},
},
null,
2
),
},
],
};
}
}
);
};
// Export session manager for use in other tools
export { SessionManager };