All checks were successful
build-and-deploy / build-and-deploy (push) Successful in 36s
- Ruta /api/mcp agregada a rutas públicas de Traefik - Validación de token Bearer en el endpoint - Token configurado como secret MCP_AUTH_TOKEN
128 lines
3.2 KiB
TypeScript
128 lines
3.2 KiB
TypeScript
// MCP Server Endpoint - JSON-RPC 2.0 over HTTP
|
|
// Protocolo MCP para agentes de IA
|
|
|
|
import { MCP_TOOLS, handleToolCall } from '../../utils/mcp'
|
|
|
|
interface JsonRpcRequest {
|
|
jsonrpc: '2.0'
|
|
id: string | number
|
|
method: string
|
|
params?: any
|
|
}
|
|
|
|
interface JsonRpcResponse {
|
|
jsonrpc: '2.0'
|
|
id: string | number | null
|
|
result?: any
|
|
error?: {
|
|
code: number
|
|
message: string
|
|
data?: any
|
|
}
|
|
}
|
|
|
|
export default defineEventHandler(async (event) => {
|
|
// Validar token de autenticación
|
|
const authHeader = getHeader(event, 'Authorization')
|
|
const expectedToken = process.env.MCP_AUTH_TOKEN
|
|
|
|
if (expectedToken) {
|
|
const providedToken = authHeader?.replace('Bearer ', '')
|
|
if (!providedToken || providedToken !== expectedToken) {
|
|
setResponseStatus(event, 401)
|
|
return createJsonRpcError(null, -32000, 'Unauthorized: Invalid or missing token')
|
|
}
|
|
}
|
|
|
|
try {
|
|
const body = await readBody(event) as JsonRpcRequest
|
|
|
|
// Validar JSON-RPC
|
|
if (body.jsonrpc !== '2.0') {
|
|
return createJsonRpcError(body.id, -32600, 'Invalid Request: jsonrpc must be "2.0"')
|
|
}
|
|
|
|
if (!body.method) {
|
|
return createJsonRpcError(body.id, -32600, 'Invalid Request: method is required')
|
|
}
|
|
|
|
// Manejar métodos MCP
|
|
switch (body.method) {
|
|
case 'initialize': {
|
|
// Handshake inicial del protocolo MCP
|
|
return createJsonRpcResponse(body.id, {
|
|
protocolVersion: '2024-11-05',
|
|
capabilities: {
|
|
tools: {}
|
|
},
|
|
serverInfo: {
|
|
name: 'printercentral-mcp',
|
|
version: '1.0.0'
|
|
}
|
|
})
|
|
}
|
|
|
|
case 'initialized': {
|
|
// Notificación de que el cliente está listo
|
|
return createJsonRpcResponse(body.id, {})
|
|
}
|
|
|
|
case 'tools/list': {
|
|
// Listar todas las tools disponibles
|
|
return createJsonRpcResponse(body.id, {
|
|
tools: MCP_TOOLS
|
|
})
|
|
}
|
|
|
|
case 'tools/call': {
|
|
// Ejecutar una tool
|
|
const { name, arguments: args } = body.params || {}
|
|
|
|
if (!name) {
|
|
return createJsonRpcError(body.id, -32602, 'Invalid params: tool name is required')
|
|
}
|
|
|
|
// Verificar que la tool existe
|
|
const tool = MCP_TOOLS.find(t => t.name === name)
|
|
if (!tool) {
|
|
return createJsonRpcError(body.id, -32602, `Tool not found: ${name}`)
|
|
}
|
|
|
|
// Ejecutar la tool
|
|
const result = await handleToolCall(name, args || {})
|
|
return createJsonRpcResponse(body.id, result)
|
|
}
|
|
|
|
case 'ping': {
|
|
return createJsonRpcResponse(body.id, {})
|
|
}
|
|
|
|
default:
|
|
return createJsonRpcError(body.id, -32601, `Method not found: ${body.method}`)
|
|
}
|
|
} catch (err: any) {
|
|
console.error('MCP Error:', err)
|
|
return createJsonRpcError(null, -32603, `Internal error: ${err.message}`)
|
|
}
|
|
})
|
|
|
|
function createJsonRpcResponse(id: string | number | null, result: any): JsonRpcResponse {
|
|
return {
|
|
jsonrpc: '2.0',
|
|
id: id ?? null,
|
|
result
|
|
}
|
|
}
|
|
|
|
function createJsonRpcError(id: string | number | null, code: number, message: string, data?: any): JsonRpcResponse {
|
|
return {
|
|
jsonrpc: '2.0',
|
|
id: id ?? null,
|
|
error: {
|
|
code,
|
|
message,
|
|
...(data && { data })
|
|
}
|
|
}
|
|
}
|