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 * as graphql from './graphql-client.js';
// Create MCP server
const server = new Server(
{
name: 'adl-mcp-server',
version: '1.0.0',
},
{
capabilities: {
tools: {},
},
}
);
// List available tools
server.setRequestHandler(ListToolsRequestSchema, async () => {
return {
tools: [
{
name: 'adl_list',
description: 'List all architectural decision log entries',
inputSchema: {
type: 'object',
properties: {},
},
},
{
name: 'adl_get',
description: 'Get a specific ADL entry by ID',
inputSchema: {
type: 'object',
properties: {
id: {
type: 'string',
description: 'The unique ID of the ADL entry',
},
},
required: ['id'],
},
},
{
name: 'adl_create',
description: 'Create a new architectural decision log entry',
inputSchema: {
type: 'object',
properties: {
author: {
type: 'string',
description: 'The author of the decision',
},
title: {
type: 'string',
description: 'The title of the decision',
},
decision: {
type: 'string',
description: 'Detailed description of the architectural decision',
},
factSheets: {
type: 'array',
items: { type: 'string' },
description: 'List of related SAP/LeanIX Fact Sheet names',
},
status: {
type: 'string',
enum: ['Proposed', 'Approved'],
description: 'Status of the decision (Proposed or Approved)',
},
},
required: ['author', 'title', 'decision', 'factSheets', 'status'],
},
},
{
name: 'adl_update',
description: 'Update an existing ADL entry',
inputSchema: {
type: 'object',
properties: {
id: {
type: 'string',
description: 'The unique ID of the ADL entry to update',
},
author: {
type: 'string',
description: 'The author of the decision',
},
title: {
type: 'string',
description: 'The title of the decision',
},
decision: {
type: 'string',
description: 'Detailed description of the architectural decision',
},
factSheets: {
type: 'array',
items: { type: 'string' },
description: 'List of related SAP/LeanIX Fact Sheet names',
},
status: {
type: 'string',
enum: ['Proposed', 'Approved'],
description: 'Status of the decision (Proposed or Approved)',
},
},
required: ['id'],
},
},
{
name: 'adl_delete',
description: 'Delete an ADL entry by ID',
inputSchema: {
type: 'object',
properties: {
id: {
type: 'string',
description: 'The unique ID of the ADL entry to delete',
},
},
required: ['id'],
},
},
{
name: 'adl_search',
description: 'Search for ADL entries by substring matching in any field (author, title, decision, factSheets) or exact status',
inputSchema: {
type: 'object',
properties: {
author: {
type: 'string',
description: 'Search substring in author field (case-insensitive)',
},
title: {
type: 'string',
description: 'Search substring in title field (case-insensitive)',
},
decision: {
type: 'string',
description: 'Search substring in decision field (case-insensitive)',
},
factSheets: {
type: 'string',
description: 'Search substring in any fact sheet (case-insensitive) - e.g., "LEA" will find "LEANIX", "Cleaner", "Learning lab"',
},
status: {
type: 'string',
enum: ['Proposed', 'Approved'],
description: 'Filter by exact status match',
},
},
},
},
],
};
});
// Handle tool calls
server.setRequestHandler(CallToolRequestSchema, async (request) => {
const { name, arguments: args } = request.params;
try {
switch (name) {
case 'adl_list': {
const entries = await graphql.getAllADLEntries();
return {
content: [
{
type: 'text',
text: JSON.stringify(entries, null, 2),
},
],
};
}
case 'adl_get': {
const entry = await graphql.getADLEntry(args.id);
if (!entry) {
return {
content: [
{
type: 'text',
text: `ADL entry with ID ${args.id} not found`,
},
],
isError: true,
};
}
return {
content: [
{
type: 'text',
text: JSON.stringify(entry, null, 2),
},
],
};
}
case 'adl_create': {
const entry = await graphql.createADLEntry({
author: args.author,
title: args.title,
decision: args.decision,
factSheets: args.factSheets,
status: args.status,
});
return {
content: [
{
type: 'text',
text: `Successfully created ADL entry:\n${JSON.stringify(entry, null, 2)}`,
},
],
};
}
case 'adl_update': {
const { id, ...updates } = args;
const entry = await graphql.updateADLEntry(id, updates);
return {
content: [
{
type: 'text',
text: `Successfully updated ADL entry:\n${JSON.stringify(entry, null, 2)}`,
},
],
};
}
case 'adl_delete': {
await graphql.deleteADLEntry(args.id);
return {
content: [
{
type: 'text',
text: `Successfully deleted ADL entry with ID: ${args.id}`,
},
],
};
}
case 'adl_search': {
const results = await graphql.searchADLEntries(args);
return {
content: [
{
type: 'text',
text: `Found ${results.length} matching entries:\n${JSON.stringify(results, null, 2)}`,
},
],
};
}
default:
return {
content: [
{
type: 'text',
text: `Unknown tool: ${name}`,
},
],
isError: true,
};
}
} catch (error) {
return {
content: [
{
type: 'text',
text: `Error: ${error.message}`,
},
],
isError: true,
};
}
});
// Start server
async function main() {
const transport = new StdioServerTransport();
await server.connect(transport);
console.error('ADL MCP Server running on stdio');
}
main().catch((error) => {
console.error('Fatal error:', error);
process.exit(1);
});