Notes MCP Server
by HeatherFlux
#!/usr/bin/env node
import { Server } from '@modelcontextprotocol/sdk/server/index.js';
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
import {
CallToolRequestSchema,
ErrorCode,
ListToolsRequestSchema,
McpError,
} from '@modelcontextprotocol/sdk/types.js';
import { Octokit } from '@octokit/rest';
import * as dotenv from 'dotenv';
import { resolve } from 'path';
// Load environment variables from .env file
dotenv.config({ path: resolve(__dirname, '../.env') });
const GITHUB_TOKEN = process.env.GITHUB_TOKEN;
if (!GITHUB_TOKEN) {
throw new Error('GITHUB_TOKEN environment variable is required');
}
interface CreateIssueArgs {
owner: string;
repo: string;
title: string;
body?: string;
labels?: string[];
assignees?: string[];
}
const isValidCreateIssueArgs = (args: any): args is CreateIssueArgs =>
typeof args === 'object' &&
args !== null &&
typeof args.owner === 'string' &&
typeof args.repo === 'string' &&
typeof args.title === 'string' &&
(args.body === undefined || typeof args.body === 'string') &&
(args.labels === undefined || Array.isArray(args.labels)) &&
(args.assignees === undefined || Array.isArray(args.assignees));
class GitHubServer {
private server: Server;
private octokit: Octokit;
constructor() {
this.server = new Server(
{
name: 'github-server',
version: '0.1.0',
},
{
capabilities: {
tools: {},
},
}
);
this.octokit = new Octokit({
auth: GITHUB_TOKEN,
});
this.setupToolHandlers();
this.server.onerror = (error) => console.error('[MCP Error]', error);
process.on('SIGINT', async () => {
await this.server.close();
process.exit(0);
});
}
private setupToolHandlers() {
this.server.setRequestHandler(ListToolsRequestSchema, async () => ({
tools: [
{
name: 'create_issue',
description: 'Create a new GitHub issue',
inputSchema: {
type: 'object',
properties: {
owner: {
type: 'string',
description: 'Repository owner (user or organization)',
},
repo: {
type: 'string',
description: 'Repository name',
},
title: {
type: 'string',
description: 'Issue title',
},
body: {
type: 'string',
description: 'Issue body/description',
},
labels: {
type: 'array',
items: {
type: 'string',
},
description: 'Labels to apply to the issue',
},
assignees: {
type: 'array',
items: {
type: 'string',
},
description: 'GitHub usernames to assign to the issue',
},
},
required: ['owner', 'repo', 'title'],
},
},
],
}));
this.server.setRequestHandler(CallToolRequestSchema, async (request) => {
if (request.params.name !== 'create_issue') {
throw new McpError(
ErrorCode.MethodNotFound,
`Unknown tool: ${request.params.name}`
);
}
if (!isValidCreateIssueArgs(request.params.arguments)) {
throw new McpError(
ErrorCode.InvalidParams,
'Invalid issue creation arguments'
);
}
try {
const response = await this.octokit.issues.create({
owner: request.params.arguments.owner,
repo: request.params.arguments.repo,
title: request.params.arguments.title,
body: request.params.arguments.body,
labels: request.params.arguments.labels,
assignees: request.params.arguments.assignees,
});
return {
content: [
{
type: 'text',
text: JSON.stringify({
issueNumber: response.data.number,
issueUrl: response.data.html_url,
}, null, 2),
},
],
};
} catch (error) {
const message = error instanceof Error ? error.message : 'Unknown error';
return {
content: [
{
type: 'text',
text: `GitHub API error: ${message}`,
},
],
isError: true,
};
}
});
}
async run() {
const transport = new StdioServerTransport();
await this.server.connect(transport);
console.error('GitHub MCP server running on stdio');
}
}
const server = new GitHubServer();
server.run().catch(console.error);