Skip to content

Commit 8363811

Browse files
committed
feat: Added some MCP functionality with mocks. Testing overall MCP flow.
1 parent 690211e commit 8363811

File tree

6 files changed

+1142
-164
lines changed

6 files changed

+1142
-164
lines changed

src/mcp/index.ts

Lines changed: 95 additions & 159 deletions
Original file line numberDiff line numberDiff line change
@@ -2,172 +2,31 @@ import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
22
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
33

44
import { VERSION } from '../config.js'
5-
import { PlanArgs, PlanOrchestrator } from '../orchestrators/plan.js';
6-
import { ValidateArgs, ValidateOrchestrator } from '../orchestrators/validate.js';
7-
import { ReporterFactory, ReporterType } from '../ui/reporters/reporter.js';
85
import apply from './tools/apply.js';
6+
import describeLanguage from './tools/describe-language.js';
7+
import getExamples from './tools/get-examples.js';
8+
import getResourceSchema from './tools/get-resource-schema.js';
9+
import listResources from './tools/list-resources.js';
910
import { registerTool } from './utils.js';
1011

1112
const server = new McpServer({
1213
name: 'codify',
13-
version: VERSION,
14-
description: 'Codify is a tool'
14+
version: '0.0.1',
15+
description: 'Codify MCP Server - A Model Context Protocol server that enables LLMs to learn and use Codify, ' +
16+
'a declarative infrastructure-as-code tool for managing development environments. Provides tools for discovering' +
17+
' resources, understanding schemas, viewing examples, and applying configurations.',
18+
}, {
19+
capabilities: {
20+
tools: true,
21+
}
1522
})
1623

17-
// Register the apply tool
24+
// Register tools
1825
registerTool(server, apply);
19-
20-
// Initialize a reporter for MCP operations
21-
const reporter = ReporterFactory.create(ReporterType.MCP);
22-
23-
/**
24-
* Register additional tools for plan, validate, and get_plan_summary
25-
*/
26-
server.registerTool(
27-
'plan',
28-
{
29-
description: 'Generate an execution plan for Codify configuration changes. This analyzes the current system state and compares it with the desired configuration to determine what changes need to be made.',
30-
inputSchema: {
31-
type: 'object',
32-
properties: {
33-
path: {
34-
type: 'string',
35-
description: 'Path to the Codify configuration file or directory. If not provided, uses the current directory.'
36-
},
37-
secureMode: {
38-
type: 'boolean',
39-
description: 'Enable secure mode to skip sensitive information in output.',
40-
default: false
41-
},
42-
verbosityLevel: {
43-
type: 'number',
44-
description: 'Set verbosity level (0-3) for detailed output.',
45-
default: 0
46-
}
47-
}
48-
}
49-
},
50-
async (params: { path?: string; secureMode?: boolean; verbosityLevel?: number }) => {
51-
try {
52-
const planArgs: PlanArgs = {
53-
path: params.path,
54-
secureMode: params.secureMode ?? false,
55-
verbosityLevel: params.verbosityLevel ?? 0,
56-
noProgress: true // Disable progress for MCP
57-
};
58-
59-
const result = await PlanOrchestrator.run(planArgs, reporter);
60-
61-
return {
62-
success: true,
63-
message: 'Plan generated successfully',
64-
planSummary: {
65-
totalResources: result.plan.resources.length,
66-
changes: result.plan.raw.map(r => ({
67-
resourceType: r.resourceType,
68-
resourceName: r.resourceName,
69-
operation: r.operation
70-
}))
71-
}
72-
};
73-
} catch (error) {
74-
const errorMessage = error instanceof Error ? error.message : String(error);
75-
return {
76-
success: false,
77-
message: 'Plan generation failed',
78-
error: errorMessage
79-
};
80-
}
81-
}
82-
);
83-
84-
server.registerTool(
85-
'validate',
86-
{
87-
description: 'Validate a Codify configuration file for syntax errors and configuration issues. This checks that the configuration is valid before applying changes.',
88-
inputSchema: {
89-
type: 'object',
90-
properties: {
91-
path: {
92-
type: 'string',
93-
description: 'Path to the Codify configuration file or directory. If not provided, uses the current directory.'
94-
},
95-
verbosityLevel: {
96-
type: 'number',
97-
description: 'Set verbosity level (0-3) for detailed output.',
98-
default: 0
99-
}
100-
}
101-
}
102-
},
103-
async (params: { path?: string; verbosityLevel?: number }) => {
104-
try {
105-
const validateArgs: ValidateArgs = {
106-
path: params.path,
107-
verbosityLevel: params.verbosityLevel ?? 0,
108-
noProgress: true // Disable progress for MCP
109-
};
110-
111-
await ValidateOrchestrator.run(validateArgs, reporter);
112-
113-
return {
114-
success: true,
115-
message: 'Configuration is valid'
116-
};
117-
} catch (error) {
118-
const errorMessage = error instanceof Error ? error.message : String(error);
119-
return {
120-
success: false,
121-
message: 'Validation failed',
122-
error: errorMessage
123-
};
124-
}
125-
}
126-
);
127-
128-
server.registerTool(
129-
'get_plan_summary',
130-
{
131-
description: 'Get a summary of what changes would be made without applying them. Useful for understanding the impact of configuration changes.',
132-
inputSchema: {
133-
type: 'object',
134-
properties: {
135-
path: {
136-
type: 'string',
137-
description: 'Path to the Codify configuration file or directory.'
138-
}
139-
}
140-
}
141-
},
142-
async (params: { path?: string }) => {
143-
try {
144-
const planArgs: PlanArgs = {
145-
path: params.path,
146-
noProgress: true
147-
};
148-
149-
const result = await PlanOrchestrator.run(planArgs, reporter);
150-
151-
return {
152-
configPath: params.path || 'current directory',
153-
totalResources: result.plan.resources.length,
154-
changes: result.plan.raw.map(r => ({
155-
type: r.resourceType,
156-
name: r.resourceName,
157-
operation: r.operation
158-
})),
159-
isEmpty: result.plan.isEmpty()
160-
};
161-
} catch (error) {
162-
const errorMessage = error instanceof Error ? error.message : String(error);
163-
return {
164-
success: false,
165-
message: 'Plan summary generation failed',
166-
error: errorMessage
167-
};
168-
}
169-
}
170-
);
26+
registerTool(server, describeLanguage);
27+
registerTool(server, getExamples);
28+
registerTool(server, getResourceSchema);
29+
registerTool(server, listResources);
17130

17231
/**
17332
* Start the MCP server
@@ -177,5 +36,82 @@ export async function startMcpServer(): Promise<void> {
17736
await server.connect(transport);
17837
console.error('Codify MCP server started');
17938
}
39+
//
40+
// export { server };
18041

181-
export { server };
42+
// export function startMcpServer(): Promise<void> {
43+
// const app = createMcpExpressApp();
44+
//
45+
// app.post('/mcp', async (req, res) => {
46+
// try {
47+
// const transport: StreamableHTTPServerTransport = new StreamableHTTPServerTransport({
48+
// sessionIdGenerator: undefined
49+
// });
50+
// await server.connect(transport);
51+
// await transport.handleRequest(req, res, req.body);
52+
// res.on('close', () => {
53+
// console.log('Request closed');
54+
// transport.close();
55+
// server.close();
56+
// });
57+
// } catch (error) {
58+
// console.error('Error handling MCP request:', error);
59+
// if (!res.headersSent) {
60+
// res.status(500).json({
61+
// jsonrpc: '2.0',
62+
// error: {
63+
// code: -32_603,
64+
// message: 'Internal server error'
65+
// },
66+
// id: null
67+
// });
68+
// }
69+
// }
70+
// });
71+
//
72+
// app.get('/mcp', async (req, res) => {
73+
// console.log('Received GET MCP request');
74+
// res.writeHead(405).end(
75+
// JSON.stringify({
76+
// jsonrpc: '2.0',
77+
// error: {
78+
// code: -32_000,
79+
// message: 'Method not allowed.'
80+
// },
81+
// id: null
82+
// })
83+
// );
84+
// });
85+
//
86+
// app.delete('/mcp', async (req, res) => {
87+
// console.log('Received DELETE MCP request');
88+
// res.writeHead(405).end(
89+
// JSON.stringify({
90+
// jsonrpc: '2.0',
91+
// error: {
92+
// code: -32_000,
93+
// message: 'Method not allowed.'
94+
// },
95+
// id: null
96+
// })
97+
// );
98+
// });
99+
//
100+
// // Start the server
101+
// const PORT = 3000;
102+
// app.listen(PORT, error => {
103+
// if (error) {
104+
// console.error('Failed to start server:', error);
105+
// process.exit(1);
106+
// }
107+
//
108+
// console.log(`MCP Stateless Streamable HTTP Server listening on port ${PORT}`);
109+
// });
110+
//
111+
// // Handle server shutdown
112+
// process.on('SIGINT', async () => {
113+
// console.log('Shutting down server...');
114+
// process.exit(0);
115+
// });
116+
//
117+
// }

src/mcp/tools/apply.ts

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -14,11 +14,11 @@ const definition = createToolDefinition({
1414
verbosityLevel: z.number().optional().default(0).describe('Set verbosity level (0-3) for detailed output.'),
1515
config: z.fromJSONSchema(ResourceSchema as any).optional()
1616
}),
17-
outputSchema: z.object({
18-
success: z.boolean().describe('Whether the apply operation was successful'),
19-
message: z.string().describe('Status message from the apply operation'),
20-
error: z.string().optional().describe('Error message if the operation failed')
21-
}),
17+
// outputSchema: z.object({
18+
// success: z.boolean().describe('Whether the apply operation was successful'),
19+
// message: z.string().describe('Status message from the apply operation'),
20+
// error: z.string().optional().describe('Error message if the operation failed')
21+
// }),
2222
},
2323
async handler(args) {
2424
try {

0 commit comments

Comments
 (0)