mirror of
https://github.com/abhigyanpatwari/GitNexus.git
synced 2026-08-28 05:25:25 +00:00
154 lines
3.9 KiB
TypeScript
154 lines
3.9 KiB
TypeScript
/**
|
|
* MCP Server
|
|
*
|
|
* Model Context Protocol server that runs on stdio.
|
|
* External AI tools (Cursor, Claude) spawn this process and
|
|
* communicate via stdin/stdout using the MCP protocol.
|
|
*
|
|
* Tools: context, search, cypher, overview, explore, impact, analyze
|
|
* Resources: context, clusters, processes, schema, cluster/{name}, process/{name}
|
|
*/
|
|
|
|
import { Server } from '@modelcontextprotocol/sdk/server/index.js';
|
|
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
|
|
import {
|
|
CallToolRequestSchema,
|
|
ListToolsRequestSchema,
|
|
ListResourcesRequestSchema,
|
|
ReadResourceRequestSchema,
|
|
ListResourceTemplatesRequestSchema,
|
|
} from '@modelcontextprotocol/sdk/types.js';
|
|
import { GITNEXUS_TOOLS } from './tools.js';
|
|
import type { LocalBackend } from './local/local-backend.js';
|
|
import { getResourceDefinitions, getResourceTemplates, readResource } from './resources.js';
|
|
|
|
export async function startMCPServer(backend: LocalBackend): Promise<void> {
|
|
const server = new Server(
|
|
{
|
|
name: 'gitnexus',
|
|
version: '0.2.0',
|
|
},
|
|
{
|
|
capabilities: {
|
|
tools: {},
|
|
resources: {},
|
|
},
|
|
}
|
|
);
|
|
|
|
// Handle list resources request
|
|
server.setRequestHandler(ListResourcesRequestSchema, async () => {
|
|
const context = backend.context;
|
|
|
|
if (!context) {
|
|
return { resources: [] };
|
|
}
|
|
|
|
const resources = getResourceDefinitions(context.projectName);
|
|
return {
|
|
resources: resources.map(r => ({
|
|
uri: r.uri,
|
|
name: r.name,
|
|
description: r.description,
|
|
mimeType: r.mimeType,
|
|
})),
|
|
};
|
|
});
|
|
|
|
// Handle list resource templates request (for dynamic resources)
|
|
server.setRequestHandler(ListResourceTemplatesRequestSchema, async () => {
|
|
const templates = getResourceTemplates();
|
|
return {
|
|
resourceTemplates: templates.map(t => ({
|
|
uriTemplate: t.uriTemplate,
|
|
name: t.name,
|
|
description: t.description,
|
|
mimeType: t.mimeType,
|
|
})),
|
|
};
|
|
});
|
|
|
|
// Handle read resource request
|
|
server.setRequestHandler(ReadResourceRequestSchema, async (request) => {
|
|
const { uri } = request.params;
|
|
|
|
try {
|
|
const content = await readResource(uri, backend);
|
|
return {
|
|
contents: [
|
|
{
|
|
uri,
|
|
mimeType: 'text/yaml',
|
|
text: content,
|
|
},
|
|
],
|
|
};
|
|
} catch (err: any) {
|
|
return {
|
|
contents: [
|
|
{
|
|
uri,
|
|
mimeType: 'text/plain',
|
|
text: `Error: ${err.message}`,
|
|
},
|
|
],
|
|
};
|
|
}
|
|
});
|
|
|
|
|
|
// Handle list tools request
|
|
server.setRequestHandler(ListToolsRequestSchema, async () => ({
|
|
tools: GITNEXUS_TOOLS.map((tool) => ({
|
|
name: tool.name,
|
|
description: tool.description,
|
|
inputSchema: tool.inputSchema,
|
|
})),
|
|
}));
|
|
|
|
// Handle tool calls
|
|
server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
const { name, arguments: args } = request.params;
|
|
|
|
try {
|
|
const result = await backend.callTool(name, args);
|
|
|
|
return {
|
|
content: [
|
|
{
|
|
type: 'text',
|
|
text: typeof result === 'string' ? result : JSON.stringify(result, null, 2),
|
|
},
|
|
],
|
|
};
|
|
} catch (error) {
|
|
const message = error instanceof Error ? error.message : 'Unknown error';
|
|
return {
|
|
content: [
|
|
{
|
|
type: 'text',
|
|
text: `Error: ${message}`,
|
|
},
|
|
],
|
|
isError: true,
|
|
};
|
|
}
|
|
});
|
|
|
|
// Connect to stdio transport
|
|
const transport = new StdioServerTransport();
|
|
await server.connect(transport);
|
|
|
|
// Handle graceful shutdown
|
|
process.on('SIGINT', async () => {
|
|
await backend.disconnect();
|
|
await server.close();
|
|
process.exit(0);
|
|
});
|
|
|
|
process.on('SIGTERM', async () => {
|
|
await backend.disconnect();
|
|
await server.close();
|
|
process.exit(0);
|
|
});
|
|
}
|