osc-mcp-server
Version:
Model Context Protocol server for OSC (Open Sound Control) endpoint management
306 lines • 13.2 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.OSCMCPServer = void 0;
const index_js_1 = require("@modelcontextprotocol/sdk/server/index.js");
const stdio_js_1 = require("@modelcontextprotocol/sdk/server/stdio.js");
const types_js_1 = require("@modelcontextprotocol/sdk/types.js");
const manager_1 = require("./osc/manager");
const validation_1 = require("./errors/validation");
const index_1 = require("./errors/index");
class OSCMCPServer {
server;
oscManager;
isRunning = false;
constructor() {
this.server = new index_js_1.Server({
name: 'osc-mcp-server',
version: '1.0.0',
});
this.oscManager = (0, manager_1.createOSCManager)();
this.setupToolHandlers();
this.setupEventHandlers();
}
async start() {
if (this.isRunning) {
throw new Error('Server is already running');
}
try {
const transport = new stdio_js_1.StdioServerTransport();
await this.server.connect(transport);
this.isRunning = true;
console.error('OSC MCP Server started successfully');
}
catch (error) {
const errorMessage = error instanceof Error ? error.message : 'Unknown error';
throw new Error(`Failed to start MCP server: ${errorMessage}`);
}
}
async shutdown() {
if (!this.isRunning) {
return;
}
try {
await this.oscManager.shutdown();
await this.server.close();
console.error('OSC MCP Server shut down successfully');
}
catch (error) {
console.error('Error during server shutdown:', error);
}
finally {
this.isRunning = false;
}
}
setupToolHandlers() {
this.server.setRequestHandler(types_js_1.ListToolsRequestSchema, async () => {
return {
tools: [
{
name: 'create_osc_endpoint',
description: 'Create a new OSC endpoint to listen for incoming OSC messages',
inputSchema: {
type: 'object',
properties: {
port: {
type: 'number',
description: 'UDP port number to listen on (1024-65535)',
minimum: 1024,
maximum: 65535,
},
bufferSize: {
type: 'number',
description: 'Maximum number of messages to store in buffer (default: 1000)',
minimum: 1,
maximum: 10000,
default: 1000,
},
addressFilters: {
type: 'array',
description: 'OSC address patterns to filter messages (optional)',
items: {
type: 'string',
},
},
},
required: ['port'],
},
},
{
name: 'stop_osc_endpoint',
description: 'Stop and remove an existing OSC endpoint',
inputSchema: {
type: 'object',
properties: {
endpointId: {
type: 'string',
description: 'ID of the endpoint to stop',
},
},
required: ['endpointId'],
},
},
{
name: 'get_osc_messages',
description: 'Query received OSC messages from endpoints',
inputSchema: {
type: 'object',
properties: {
endpointId: {
type: 'string',
description: 'Optional endpoint ID to query (if not provided, queries all endpoints)',
},
addressPattern: {
type: 'string',
description: 'Optional OSC address pattern to filter messages',
},
timeWindowSeconds: {
type: 'number',
description: 'Optional time window in seconds (from now backwards)',
minimum: 1,
},
limit: {
type: 'number',
description: 'Optional maximum number of messages to return',
minimum: 1,
maximum: 1000,
},
},
required: [],
},
},
{
name: 'get_endpoint_status',
description: 'Get status information for OSC endpoints',
inputSchema: {
type: 'object',
properties: {
endpointId: {
type: 'string',
description: 'Optional endpoint ID (if not provided, returns all endpoints)',
},
},
required: [],
},
},
],
};
});
this.server.setRequestHandler(types_js_1.CallToolRequestSchema, async (request) => {
const { name, arguments: args } = request.params;
try {
switch (name) {
case 'create_osc_endpoint':
return await this.handleCreateEndpoint(args);
case 'stop_osc_endpoint':
return await this.handleStopEndpoint(args);
case 'get_osc_messages':
return await this.handleGetMessages(args);
case 'get_endpoint_status':
return await this.handleGetEndpointStatus(args);
default:
throw new types_js_1.McpError(types_js_1.ErrorCode.MethodNotFound, `Unknown tool: ${name}`);
}
}
catch (error) {
if (error instanceof types_js_1.McpError) {
throw error;
}
const errorMessage = error instanceof Error ? error.message : 'Unknown error';
throw new types_js_1.McpError(types_js_1.ErrorCode.InternalError, `Tool execution failed: ${errorMessage}`);
}
});
}
setupEventHandlers() {
this.oscManager.on('endpointCreated', endpointInfo => {
console.error(`Endpoint created: ${endpointInfo.id} on port ${endpointInfo.port}`);
});
this.oscManager.on('endpointStopped', endpointId => {
console.error(`Endpoint stopped: ${endpointId}`);
});
this.oscManager.on('endpointError', (endpointId, error) => {
console.error(`Endpoint error (${endpointId}):`, error);
});
this.oscManager.on('messageReceived', (endpointId, message) => {
console.error(`Message received on ${endpointId}: ${message.address}`);
});
}
async handleCreateEndpoint(params) {
const validation = validation_1.ParameterValidator.validateCreateEndpoint(params);
if (!validation.isValid) {
throw new types_js_1.McpError(types_js_1.ErrorCode.InvalidParams, validation.error.message, validation.error.details);
}
const config = {
port: params.port,
bufferSize: params.bufferSize || 1000,
addressFilters: params.addressFilters || [],
};
try {
const response = await this.oscManager.createEndpoint(config);
if (response.status === 'error') {
throw new types_js_1.McpError(types_js_1.ErrorCode.InternalError, response.message);
}
return {
content: [
{
type: 'text',
text: JSON.stringify(response, null, 2),
},
],
};
}
catch (error) {
if (error instanceof types_js_1.McpError) {
throw error;
}
const oscError = index_1.OperationErrors.operationFailed('create_osc_endpoint', error instanceof Error ? error.message : 'Unknown error');
throw new types_js_1.McpError(types_js_1.ErrorCode.InternalError, oscError.message, oscError.details);
}
}
async handleStopEndpoint(params) {
const validation = validation_1.ParameterValidator.validateStopEndpoint(params);
if (!validation.isValid) {
throw new types_js_1.McpError(types_js_1.ErrorCode.InvalidParams, validation.error.message, validation.error.details);
}
try {
const response = await this.oscManager.stopEndpoint(params.endpointId);
return {
content: [
{
type: 'text',
text: JSON.stringify(response, null, 2),
},
],
};
}
catch (error) {
const oscError = index_1.OperationErrors.operationFailed('stop_osc_endpoint', error instanceof Error ? error.message : 'Unknown error');
throw new types_js_1.McpError(types_js_1.ErrorCode.InternalError, oscError.message, oscError.details);
}
}
async handleGetMessages(params) {
const validation = validation_1.ParameterValidator.validateGetMessages(params);
if (!validation.isValid) {
throw new types_js_1.McpError(types_js_1.ErrorCode.InvalidParams, validation.error.message, validation.error.details);
}
try {
const query = {};
if (params.addressPattern) {
query.addressPattern = params.addressPattern;
}
if (params.timeWindowSeconds) {
query.since = new Date(Date.now() - params.timeWindowSeconds * 1000);
}
if (params.limit) {
query.limit = params.limit;
}
const response = this.oscManager.getMessages(params.endpointId, query);
return {
content: [
{
type: 'text',
text: JSON.stringify(response, this.dateReplacer, 2),
},
],
};
}
catch (error) {
const oscError = index_1.OperationErrors.operationFailed('get_osc_messages', error instanceof Error ? error.message : 'Unknown error');
throw new types_js_1.McpError(types_js_1.ErrorCode.InternalError, oscError.message, oscError.details);
}
}
async handleGetEndpointStatus(params) {
const validation = validation_1.ParameterValidator.validateGetEndpointStatus(params);
if (!validation.isValid) {
throw new types_js_1.McpError(types_js_1.ErrorCode.InvalidParams, validation.error.message, validation.error.details);
}
try {
const response = this.oscManager.getEndpointStatus(params.endpointId);
return {
content: [
{
type: 'text',
text: JSON.stringify(response, this.dateReplacer, 2),
},
],
};
}
catch (error) {
const oscError = index_1.OperationErrors.operationFailed('get_endpoint_status', error instanceof Error ? error.message : 'Unknown error');
throw new types_js_1.McpError(types_js_1.ErrorCode.InternalError, oscError.message, oscError.details);
}
}
isServerRunning() {
return this.isRunning;
}
getOSCManager() {
return this.oscManager;
}
dateReplacer(_key, value) {
if (value instanceof Date) {
return value.toISOString();
}
return value;
}
}
exports.OSCMCPServer = OSCMCPServer;
//# sourceMappingURL=server.js.map