leanix-pathfinder-mcp-server
Version:
MCP Server for LeanIX Pathfinder API - Enterprise Architecture Management Platform
923 lines • 32.4 kB
JavaScript
#!/usr/bin/env node
import { Server } from '@modelcontextprotocol/sdk/server/index.js';
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
import { CallToolRequestSchema, ListToolsRequestSchema, } from '@modelcontextprotocol/sdk/types.js';
import { LeanIXClient } from './client.js';
// Server configuration
const server = new Server({
name: 'leanix-pathfinder-mcp-server',
version: '1.0.0',
}, {
capabilities: {
tools: {},
},
});
// Initialize LeanIX client
let leanixClient = null;
function initializeClient() {
if (!leanixClient) {
const config = {
baseUrl: process.env.LEANIX_BASE_URL || '',
clientId: process.env.LEANIX_CLIENT_ID || '',
clientSecret: process.env.LEANIX_CLIENT_SECRET || '',
workspaceId: process.env.LEANIX_WORKSPACE_ID,
};
if (!config.baseUrl || !config.clientId || !config.clientSecret) {
throw new Error('Missing required LeanIX configuration. Please set LEANIX_BASE_URL, LEANIX_CLIENT_ID, and LEANIX_CLIENT_SECRET environment variables.');
}
leanixClient = new LeanIXClient(config);
}
return leanixClient;
}
// Tool definitions
const tools = [
// Fact Sheets
{
name: 'leanix_get_fact_sheet',
description: 'Get a specific fact sheet by ID',
inputSchema: {
type: 'object',
properties: {
id: {
type: 'string',
description: 'The ID of the fact sheet to retrieve',
},
version: {
type: 'number',
description: 'Optional version number',
},
},
required: ['id'],
},
},
{
name: 'leanix_list_fact_sheets',
description: 'List fact sheets with optional filtering',
inputSchema: {
type: 'object',
properties: {
types: {
type: 'string',
description: 'Comma-separated list of fact sheet types to filter by',
},
ids: {
type: 'string',
description: 'Comma-separated list of fact sheet IDs',
},
relations: {
type: 'string',
description: 'Include relations in response',
},
pageSize: {
type: 'number',
description: 'Number of results per page',
},
cursor: {
type: 'string',
description: 'Cursor for pagination',
},
},
},
},
{
name: 'leanix_create_fact_sheet',
description: 'Create a new fact sheet',
inputSchema: {
type: 'object',
properties: {
factSheet: {
type: 'object',
description: 'The fact sheet data to create',
properties: {
name: { type: 'string' },
type: { type: 'string' },
description: { type: 'string' },
displayName: { type: 'string' },
},
required: ['name', 'type'],
},
},
required: ['factSheet'],
},
},
{
name: 'leanix_update_fact_sheet',
description: 'Update an existing fact sheet',
inputSchema: {
type: 'object',
properties: {
id: {
type: 'string',
description: 'The ID of the fact sheet to update',
},
factSheet: {
type: 'object',
description: 'The fact sheet data to update',
},
relationTypes: {
type: 'string',
description: 'Comma-separated list of relation types to update',
},
},
required: ['id', 'factSheet'],
},
},
{
name: 'leanix_delete_fact_sheet',
description: 'Delete a fact sheet',
inputSchema: {
type: 'object',
properties: {
id: {
type: 'string',
description: 'The ID of the fact sheet to delete',
},
comment: {
type: 'string',
description: 'Optional comment for the deletion',
},
},
required: ['id'],
},
},
{
name: 'leanix_get_fact_sheet_relations',
description: 'Get relations for a specific fact sheet',
inputSchema: {
type: 'object',
properties: {
id: {
type: 'string',
description: 'The ID of the fact sheet',
},
relationType: {
type: 'string',
description: 'Filter by relation type',
},
withFactSheets: {
type: 'boolean',
description: 'Include fact sheet data in relations',
},
},
required: ['id'],
},
},
// Bookmarks
{
name: 'leanix_list_bookmarks',
description: 'List all bookmarks',
inputSchema: {
type: 'object',
properties: {
bookmarkType: {
type: 'string',
description: 'Filter by bookmark type (INVENTORY, REPORTING, VISUALIZER, DASHBOARD, INVENTORY_EXPORT)',
},
pageSize: {
type: 'number',
description: 'Number of results per page',
},
cursor: {
type: 'string',
description: 'Cursor for pagination',
},
},
},
},
{
name: 'leanix_get_bookmark',
description: 'Get a specific bookmark by ID',
inputSchema: {
type: 'object',
properties: {
id: {
type: 'string',
description: 'The ID of the bookmark to retrieve',
},
markAsViewed: {
type: 'boolean',
description: 'Mark the bookmark as viewed',
},
version: {
type: 'number',
description: 'Optional version number',
},
},
required: ['id'],
},
},
{
name: 'leanix_create_bookmark',
description: 'Create a new bookmark',
inputSchema: {
type: 'object',
properties: {
bookmark: {
type: 'object',
description: 'The bookmark data to create',
properties: {
name: { type: 'string' },
type: { type: 'string' },
description: { type: 'string' },
permittedReadUserIds: { type: 'array', items: { type: 'string' } },
permittedWriteUserIds: { type: 'array', items: { type: 'string' } },
referencedFactSheetIds: { type: 'array', items: { type: 'string' } },
},
required: ['name', 'type', 'permittedReadUserIds', 'permittedWriteUserIds', 'referencedFactSheetIds'],
},
},
required: ['bookmark'],
},
},
{
name: 'leanix_update_bookmark',
description: 'Update an existing bookmark',
inputSchema: {
type: 'object',
properties: {
id: {
type: 'string',
description: 'The ID of the bookmark to update',
},
bookmark: {
type: 'object',
description: 'The bookmark data to update',
},
},
required: ['id', 'bookmark'],
},
},
{
name: 'leanix_delete_bookmark',
description: 'Delete a bookmark',
inputSchema: {
type: 'object',
properties: {
id: {
type: 'string',
description: 'The ID of the bookmark to delete',
},
},
required: ['id'],
},
},
{
name: 'leanix_share_bookmark',
description: 'Share a bookmark with users',
inputSchema: {
type: 'object',
properties: {
bookmarkId: {
type: 'string',
description: 'The ID of the bookmark to share',
},
sharedWithUserIds: {
type: 'array',
items: { type: 'string' },
description: 'Array of user IDs to share with',
},
notifyUsers: {
type: 'boolean',
description: 'Whether to notify users about the share',
},
},
required: ['bookmarkId', 'sharedWithUserIds'],
},
},
// Models
{
name: 'leanix_get_data_model',
description: 'Get the data model for the workspace',
inputSchema: {
type: 'object',
properties: {
workspaceId: {
type: 'string',
description: 'Optional workspace ID (uses default if not provided)',
},
useCaseId: {
type: 'string',
description: 'Optional use case ID',
},
},
},
},
{
name: 'leanix_get_view_model',
description: 'Get the view model for the workspace',
inputSchema: {
type: 'object',
properties: {
workspaceId: {
type: 'string',
description: 'Optional workspace ID (uses default if not provided)',
},
useCaseId: {
type: 'string',
description: 'Optional use case ID',
},
},
},
},
{
name: 'leanix_get_authorization',
description: 'Get authorization roles and permissions',
inputSchema: {
type: 'object',
properties: {
workspaceId: {
type: 'string',
description: 'Optional workspace ID (uses default if not provided)',
},
useCaseId: {
type: 'string',
description: 'Optional use case ID',
},
},
},
},
{
name: 'leanix_get_meta_model',
description: 'Get the meta model',
inputSchema: {
type: 'object',
properties: {
factSheetType: {
type: 'string',
description: 'Optional fact sheet type to filter the meta model',
},
},
},
},
// GraphQL
{
name: 'leanix_execute_graphql',
description: 'Execute a GraphQL query or mutation',
inputSchema: {
type: 'object',
properties: {
query: {
type: 'string',
description: 'The GraphQL query or mutation string',
},
variables: {
type: 'object',
description: 'Optional variables for the GraphQL query',
},
operationName: {
type: 'string',
description: 'Optional operation name for multi-operation queries',
},
},
required: ['query'],
},
},
// Features
{
name: 'leanix_list_features',
description: 'List all workspace features',
inputSchema: {
type: 'object',
properties: {},
},
},
{
name: 'leanix_get_feature',
description: 'Get a specific feature by ID',
inputSchema: {
type: 'object',
properties: {
id: {
type: 'string',
description: 'The ID of the feature to retrieve',
},
},
required: ['id'],
},
},
{
name: 'leanix_update_feature',
description: 'Update a feature (Admin role only)',
inputSchema: {
type: 'object',
properties: {
id: {
type: 'string',
description: 'The ID of the feature to update',
},
enabled: {
type: 'boolean',
description: 'Whether the feature should be enabled',
},
},
required: ['id'],
},
},
// Settings
{
name: 'leanix_get_settings',
description: 'Get workspace settings',
inputSchema: {
type: 'object',
properties: {
workspaceId: {
type: 'string',
description: 'Optional workspace ID (uses default if not provided)',
},
},
},
},
{
name: 'leanix_update_settings',
description: 'Update workspace settings',
inputSchema: {
type: 'object',
properties: {
settings: {
type: 'object',
description: 'The settings to update',
},
},
required: ['settings'],
},
},
// Exports
{
name: 'leanix_list_exports',
description: 'List available exports',
inputSchema: {
type: 'object',
properties: {
type: {
type: 'string',
description: 'Filter by export type',
},
pageSize: {
type: 'number',
description: 'Number of results per page',
},
cursor: {
type: 'string',
description: 'Cursor for pagination',
},
},
},
},
{
name: 'leanix_create_export',
description: 'Create a new export',
inputSchema: {
type: 'object',
properties: {
exportConfig: {
type: 'object',
description: 'Export configuration',
properties: {
type: { type: 'string' },
factSheetTypes: { type: 'string' },
bookmarkId: { type: 'string' },
},
},
},
required: ['exportConfig'],
},
},
// Suggestions
{
name: 'leanix_get_suggestions',
description: 'Get search suggestions for a given term',
inputSchema: {
type: 'object',
properties: {
q: {
type: 'string',
description: 'Search term (2-1000 characters)',
},
object: {
type: 'string',
description: 'Suggestions object type (default: factSheet)',
},
count: {
type: 'number',
description: 'Number of suggestions (1-100)',
},
perType: {
type: 'boolean',
description: 'Group suggestions per object type',
},
useCaseId: {
type: 'string',
description: 'Optional use case ID',
},
},
required: ['q'],
},
},
];
// List tools handler
server.setRequestHandler(ListToolsRequestSchema, async () => {
return {
tools,
};
});
// Call tool handler
server.setRequestHandler(CallToolRequestSchema, async (request) => {
const { name, arguments: args } = request.params;
try {
const client = initializeClient();
let result;
switch (name) {
// Fact Sheets
case 'leanix_get_fact_sheet': {
const { id, version } = args;
const queryParams = client.buildQueryParams({ version });
const response = await client.get(`/services/pathfinder/v1/factSheets/${id}${queryParams}`);
result = {
content: [
{
type: 'text',
text: JSON.stringify(response.data, null, 2),
},
],
};
break;
}
case 'leanix_list_fact_sheets': {
const { types, ids, relations, pageSize, cursor } = args;
const queryParams = client.buildQueryParams({ types, ids, relations, pageSize, cursor });
const response = await client.get(`/services/pathfinder/v1/factSheets${queryParams}`);
result = {
content: [
{
type: 'text',
text: JSON.stringify(response.data, null, 2),
},
],
};
break;
}
case 'leanix_create_fact_sheet': {
const { factSheet } = args;
const response = await client.post('/services/pathfinder/v1/factSheets', factSheet);
result = {
content: [
{
type: 'text',
text: JSON.stringify(response.data, null, 2),
},
],
};
break;
}
case 'leanix_update_fact_sheet': {
const { id, factSheet, relationTypes } = args;
const queryParams = client.buildQueryParams({ relationTypes });
const response = await client.put(`/services/pathfinder/v1/factSheets/${id}${queryParams}`, factSheet);
result = {
content: [
{
type: 'text',
text: JSON.stringify(response.data, null, 2),
},
],
};
break;
}
case 'leanix_delete_fact_sheet': {
const { id, comment } = args;
const body = comment ? { comment } : undefined;
const response = await client.delete(`/services/pathfinder/v1/factSheets/${id}`, { body });
result = {
content: [
{
type: 'text',
text: `Fact sheet ${id} deleted successfully`,
},
],
};
break;
}
case 'leanix_get_fact_sheet_relations': {
const { id, relationType, withFactSheets } = args;
const queryParams = client.buildQueryParams({ relationType, withFactSheets });
const response = await client.get(`/services/pathfinder/v1/factSheets/${id}/relations${queryParams}`);
result = {
content: [
{
type: 'text',
text: JSON.stringify(response.data, null, 2),
},
],
};
break;
}
// Bookmarks
case 'leanix_list_bookmarks': {
const { bookmarkType, pageSize, cursor } = args;
const queryParams = client.buildQueryParams({ bookmarkType, pageSize, cursor });
const response = await client.get(`/services/pathfinder/v1/bookmarks${queryParams}`);
result = {
content: [
{
type: 'text',
text: JSON.stringify(response.data, null, 2),
},
],
};
break;
}
case 'leanix_get_bookmark': {
const { id, markAsViewed, version } = args;
const queryParams = client.buildQueryParams({ markAsViewed, version });
const response = await client.get(`/services/pathfinder/v1/bookmarks/${id}${queryParams}`);
result = {
content: [
{
type: 'text',
text: JSON.stringify(response.data, null, 2),
},
],
};
break;
}
case 'leanix_create_bookmark': {
const { bookmark } = args;
const response = await client.post('/services/pathfinder/v1/bookmarks', bookmark);
result = {
content: [
{
type: 'text',
text: JSON.stringify(response.data, null, 2),
},
],
};
break;
}
case 'leanix_update_bookmark': {
const { id, bookmark } = args;
const response = await client.put(`/services/pathfinder/v1/bookmarks/${id}`, bookmark);
result = {
content: [
{
type: 'text',
text: JSON.stringify(response.data, null, 2),
},
],
};
break;
}
case 'leanix_delete_bookmark': {
const { id } = args;
await client.delete(`/services/pathfinder/v1/bookmarks/${id}`);
result = {
content: [
{
type: 'text',
text: `Bookmark ${id} deleted successfully`,
},
],
};
break;
}
case 'leanix_share_bookmark': {
const { bookmarkId, sharedWithUserIds, notifyUsers } = args;
const shareData = {
bookmarkId,
sharedWithUserIds,
notifyUsers: notifyUsers || false,
};
const response = await client.post('/services/pathfinder/v1/bookmarkShares', shareData);
result = {
content: [
{
type: 'text',
text: JSON.stringify(response.data, null, 2),
},
],
};
break;
}
// Models
case 'leanix_get_data_model': {
const { workspaceId, useCaseId } = args;
const queryParams = client.buildQueryParams({ workspaceId, useCaseId });
const response = await client.get(`/services/pathfinder/v1/models/dataModel${queryParams}`);
result = {
content: [
{
type: 'text',
text: JSON.stringify(response.data, null, 2),
},
],
};
break;
}
case 'leanix_get_view_model': {
const { workspaceId, useCaseId } = args;
const queryParams = client.buildQueryParams({ workspaceId, useCaseId });
const response = await client.get(`/services/pathfinder/v1/models/viewModel${queryParams}`);
result = {
content: [
{
type: 'text',
text: JSON.stringify(response.data, null, 2),
},
],
};
break;
}
case 'leanix_get_authorization': {
const { workspaceId, useCaseId } = args;
const queryParams = client.buildQueryParams({ workspaceId, useCaseId });
const response = await client.get(`/services/pathfinder/v1/models/authorization${queryParams}`);
result = {
content: [
{
type: 'text',
text: JSON.stringify(response.data, null, 2),
},
],
};
break;
}
case 'leanix_get_meta_model': {
const { factSheetType } = args;
let url = '/services/pathfinder/v1/models/metaModel';
if (factSheetType) {
url += `/${factSheetType}`;
}
const response = await client.get(url);
result = {
content: [
{
type: 'text',
text: JSON.stringify(response.data, null, 2),
},
],
};
break;
}
// GraphQL
case 'leanix_execute_graphql': {
const request = args;
if (!request.query) {
throw new Error('GraphQL query is required');
}
const { query, variables, operationName } = request;
const graphqlRequest = {
query,
variables,
operationName,
};
const response = await client.post('/services/pathfinder/v1/graphql', graphqlRequest);
result = {
content: [
{
type: 'text',
text: JSON.stringify(response.data, null, 2),
},
],
};
break;
}
// Features
case 'leanix_list_features': {
const response = await client.get('/services/pathfinder/v1/features');
result = {
content: [
{
type: 'text',
text: JSON.stringify(response.data, null, 2),
},
],
};
break;
}
case 'leanix_get_feature': {
const { id } = args;
const response = await client.get(`/services/pathfinder/v1/features/${id}`);
result = {
content: [
{
type: 'text',
text: JSON.stringify(response.data, null, 2),
},
],
};
break;
}
case 'leanix_update_feature': {
const { id, enabled } = args;
const queryParams = client.buildQueryParams({ enabled });
const response = await client.post(`/services/pathfinder/v1/features/${id}${queryParams}`);
result = {
content: [
{
type: 'text',
text: JSON.stringify(response.data, null, 2),
},
],
};
break;
}
// Settings
case 'leanix_get_settings': {
const { workspaceId } = args;
const queryParams = client.buildQueryParams({ workspaceId });
const response = await client.get(`/services/pathfinder/v1/settings${queryParams}`);
result = {
content: [
{
type: 'text',
text: JSON.stringify(response.data, null, 2),
},
],
};
break;
}
case 'leanix_update_settings': {
const { settings } = args;
const response = await client.put('/services/pathfinder/v1/settings', settings);
result = {
content: [
{
type: 'text',
text: JSON.stringify(response.data, null, 2),
},
],
};
break;
}
// Exports
case 'leanix_list_exports': {
const { type, pageSize, cursor } = args;
const queryParams = client.buildQueryParams({ type, pageSize, cursor });
const response = await client.get(`/services/pathfinder/v1/exports${queryParams}`);
result = {
content: [
{
type: 'text',
text: JSON.stringify(response.data, null, 2),
},
],
};
break;
}
case 'leanix_create_export': {
const { exportConfig } = args;
const response = await client.post('/services/pathfinder/v1/exports', exportConfig);
result = {
content: [
{
type: 'text',
text: JSON.stringify(response.data, null, 2),
},
],
};
break;
}
// Suggestions
case 'leanix_get_suggestions': {
const { q, object, count, perType, useCaseId } = args;
const queryParams = client.buildQueryParams({ q, object, count, perType, useCaseId });
const response = await client.get(`/services/pathfinder/v1/suggestions${queryParams}`);
result = {
content: [
{
type: 'text',
text: JSON.stringify(response.data, null, 2),
},
],
};
break;
}
default:
throw new Error(`Unknown tool: ${name}`);
}
return result;
}
catch (error) {
const errorMessage = error.response?.data?.errorMessage || error.message || 'Unknown error occurred';
const statusCode = error.response?.status || 500;
return {
content: [
{
type: 'text',
text: `Error ${statusCode}: ${errorMessage}`,
},
],
isError: true,
};
}
});
// Start the server
async function main() {
const transport = new StdioServerTransport();
await server.connect(transport);
console.error('LeanIX Pathfinder MCP Server started');
}
main().catch((error) => {
console.error('Server failed to start:', error);
process.exit(1);
});
//# sourceMappingURL=index.js.map