From 4b5ea319915171b08318d623e11c88893cb1ef42 Mon Sep 17 00:00:00 2001 From: Eugeny Date: Wed, 12 Aug 2026 02:02:23 +0300 Subject: [PATCH 1/5] feat(mcp): add initial mcp server setup --- package.json | 2 ++ src/index.ts | 3 ++ src/integrations/mcp/index.ts | 9 ++++++ src/integrations/mcp/mcp.ts | 54 +++++++++++++++++++++++++++++++++++ yarn.lock | 32 +++++++++++++++++++++ 5 files changed, 100 insertions(+) create mode 100644 src/integrations/mcp/index.ts create mode 100644 src/integrations/mcp/mcp.ts diff --git a/package.json b/package.json index 6f67f5d4..b08f3f67 100644 --- a/package.json +++ b/package.json @@ -43,6 +43,8 @@ "@graphql-tools/utils": "^8.9.0", "@hawk.so/nodejs": "^3.3.2", "@hawk.so/types": "^0.5.9", + "@modelcontextprotocol/node": "^2.0.0", + "@modelcontextprotocol/server": "^2.0.0", "@n1ru4l/json-patch-plus": "^0.2.0", "@node-saml/node-saml": "^5.0.1", "@octokit/oauth-methods": "^4.0.0", diff --git a/src/index.ts b/src/index.ts index cb6f8d93..4c3af212 100644 --- a/src/index.ts +++ b/src/index.ts @@ -32,6 +32,7 @@ import ReleasesFactory from './models/releasesFactory'; import RedisHelper from './redisHelper'; import { appendSsoRoutes } from './sso'; import { appendGitHubRoutes } from './integrations/github'; +import { appendMCPRoutes } from './integrations/mcp'; /** * Option to enable playground @@ -272,6 +273,8 @@ class HawkAPI { */ appendGitHubRoutes(this.app, sharedFactories); + appendMCPRoutes(this.app, sharedFactories); + await this.server.start(); this.app.use(graphqlUploadExpress()); this.server.applyMiddleware({ app: this.app }); diff --git a/src/integrations/mcp/index.ts b/src/integrations/mcp/index.ts new file mode 100644 index 00000000..2053de3c --- /dev/null +++ b/src/integrations/mcp/index.ts @@ -0,0 +1,9 @@ +import express from 'express'; +import { ContextFactories } from 'src/types/graphql'; +import { createMCPRouter } from "./mcp"; + +export function appendMCPRoutes(app: express.Application, factories: ContextFactories): void { + const router = createMCPRouter(factories); + + app.use('/integration/mcp', router); +} diff --git a/src/integrations/mcp/mcp.ts b/src/integrations/mcp/mcp.ts new file mode 100644 index 00000000..378daa27 --- /dev/null +++ b/src/integrations/mcp/mcp.ts @@ -0,0 +1,54 @@ +import express from "express"; +import { McpServer } from '@modelcontextprotocol/server'; +import { NodeStreamableHTTPServerTransport } from '@modelcontextprotocol/node'; +import { ContextFactories } from 'src/types/graphql'; + + +const server = new McpServer({ + name: "server", + version: "0.0.0", + description: "A test server" +}); + +server.registerTool( + "hello_world", + { + description: "A test tool to greet the user" + }, + async () => { + return { + content: [ + { + type: "text" as const, + text: "Hi" + } + ] + } + } +) + +const transport = new NodeStreamableHTTPServerTransport({ sessionIdGenerator: undefined }); + +server.connect(transport); + +export const createMCPRouter = (factories: ContextFactories): express.Router => { + const router = express.Router(); + + router.post("/", async (req, res, next) => { + try { + await transport.handleRequest(req, res, req.body); + } catch (error) { + next(error); + } + }); + + router.post("/", async (req, res, next) => { + try { + await transport.handleRequest(req, res, req.body); + } catch (error) { + next(error); + } + }) + + return router; +}; diff --git a/yarn.lock b/yarn.lock index 995f6267..ed01389f 100644 --- a/yarn.lock +++ b/yarn.lock @@ -517,6 +517,11 @@ dependencies: bson "^7.0.0" +"@hono/node-server@^1.19.9": + version "1.19.17" + resolved "https://registry.yarnpkg.com/@hono/node-server/-/node-server-1.19.17.tgz#99bd8625cdbae82652a135c71363cd6322fae483" + integrity sha512-dSneS5qhiauZWGDCeK4o695Xd9nUNjviSZCMQrj10eetr8Uln1ucn6bbphOM6UynAMMtNIzZNSpL9vnASJwrPQ== + "@isaacs/cliui@^8.0.2": version "8.0.2" resolved "https://registry.yarnpkg.com/@isaacs/cliui/-/cliui-8.0.2.tgz#b37667b7bc181c168782259bab42474fbf52b550" @@ -855,6 +860,28 @@ "@jridgewell/resolve-uri" "^3.1.0" "@jridgewell/sourcemap-codec" "^1.4.14" +"@modelcontextprotocol/core@2.0.0": + version "2.0.0" + resolved "https://registry.yarnpkg.com/@modelcontextprotocol/core/-/core-2.0.0.tgz#c918a4c6aef22a7c59dfb0aabc2a82c187cce157" + integrity sha512-pJCEwGG7Lfr/+PQp9ZTwKXNeO5wzbfKL7H3MYpCorM4oFBoQrdjnBgEoqG+RjhsvS1FKrDbKux+M1HhlnGWqcA== + dependencies: + zod "^4.2.0" + +"@modelcontextprotocol/node@^2.0.0": + version "2.0.0" + resolved "https://registry.yarnpkg.com/@modelcontextprotocol/node/-/node-2.0.0.tgz#3851275cb1c08aeb113d5b7cbe6dbe7adab37c65" + integrity sha512-Y4hAC2XdGDUdDOCbLDOCA4+aL3NUldjsOWlDL/YwpAxrPhRm1xHd7lZ+mLacvZ9t3PaH28wgNoaLQGrIk1P2pg== + dependencies: + "@hono/node-server" "^1.19.9" + +"@modelcontextprotocol/server@^2.0.0": + version "2.0.0" + resolved "https://registry.yarnpkg.com/@modelcontextprotocol/server/-/server-2.0.0.tgz#4fae5ccb8f7925ed9a6bf6595bb3b58617063ffd" + integrity sha512-YhHWdHfpFMQfd0prsEnxKeS3Qz3ytIGmsS0sth4KDjnacIT7hxk6hXHkJ9KysxlkvTM+WZAtQbbcUhdoP4Hvtw== + dependencies: + "@modelcontextprotocol/core" "2.0.0" + zod "^4.2.0" + "@mongodb-js/saslprep@^1.3.0": version "1.4.4" resolved "https://registry.yarnpkg.com/@mongodb-js/saslprep/-/saslprep-1.4.4.tgz#34a946ff6ae142e8f2259b87f2935f8284ba874d" @@ -7131,3 +7158,8 @@ zod@^3.25.76: version "3.25.76" resolved "https://registry.yarnpkg.com/zod/-/zod-3.25.76.tgz#26841c3f6fd22a6a2760e7ccb719179768471e34" integrity sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ== + +zod@^4.2.0: + version "4.4.3" + resolved "https://registry.yarnpkg.com/zod/-/zod-4.4.3.tgz#b680f172885d18bbebf21a834ea25e55a1bbf356" + integrity sha512-ytENFjIJFl2UwYglde2jchW2Hwm4GJFLDiSXWdTrJQBIN9Fcyp7n4DhxJEiWNAJMV1/BqWfW/kkg71UDcHJyTQ== From 6b6996c07963aaa138fc63416779d76d6f871025 Mon Sep 17 00:00:00 2001 From: Eugeny Date: Wed, 12 Aug 2026 02:10:11 +0300 Subject: [PATCH 2/5] remove duplication --- src/integrations/mcp/mcp.ts | 8 -------- 1 file changed, 8 deletions(-) diff --git a/src/integrations/mcp/mcp.ts b/src/integrations/mcp/mcp.ts index 378daa27..76ff61c2 100644 --- a/src/integrations/mcp/mcp.ts +++ b/src/integrations/mcp/mcp.ts @@ -42,13 +42,5 @@ export const createMCPRouter = (factories: ContextFactories): express.Router => } }); - router.post("/", async (req, res, next) => { - try { - await transport.handleRequest(req, res, req.body); - } catch (error) { - next(error); - } - }) - return router; }; From a8ac3173ab72b92c949854b6312ca41609c1e081 Mon Sep 17 00:00:00 2001 From: Eugeny Date: Wed, 19 Aug 2026 02:15:36 +0300 Subject: [PATCH 3/5] chore(mcp): add comments, fix transport single instance issue --- src/index.ts | 4 ++ src/integrations/mcp/index.ts | 6 +++ src/integrations/mcp/mcp.ts | 78 ++++++++++++++++++++++------------- 3 files changed, 60 insertions(+), 28 deletions(-) diff --git a/src/index.ts b/src/index.ts index 4c3af212..56984e2c 100644 --- a/src/index.ts +++ b/src/index.ts @@ -273,6 +273,10 @@ class HawkAPI { */ appendGitHubRoutes(this.app, sharedFactories); + /** + * Append MCP integration routes to Express app using shared factories + * Note: This must be called after database connections are established + */ appendMCPRoutes(this.app, sharedFactories); await this.server.start(); diff --git a/src/integrations/mcp/index.ts b/src/integrations/mcp/index.ts index 2053de3c..e7bc2da5 100644 --- a/src/integrations/mcp/index.ts +++ b/src/integrations/mcp/index.ts @@ -2,6 +2,12 @@ import express from 'express'; import { ContextFactories } from 'src/types/graphql'; import { createMCPRouter } from "./mcp"; +/** + * Append MCP route to Express App + * + * @param app - Express application instance + * @param factories - context factories for database access + */ export function appendMCPRoutes(app: express.Application, factories: ContextFactories): void { const router = createMCPRouter(factories); diff --git a/src/integrations/mcp/mcp.ts b/src/integrations/mcp/mcp.ts index 76ff61c2..7fcc5386 100644 --- a/src/integrations/mcp/mcp.ts +++ b/src/integrations/mcp/mcp.ts @@ -3,39 +3,28 @@ import { McpServer } from '@modelcontextprotocol/server'; import { NodeStreamableHTTPServerTransport } from '@modelcontextprotocol/node'; import { ContextFactories } from 'src/types/graphql'; - -const server = new McpServer({ - name: "server", - version: "0.0.0", - description: "A test server" -}); - -server.registerTool( - "hello_world", - { - description: "A test tool to greet the user" - }, - async () => { - return { - content: [ - { - type: "text" as const, - text: "Hi" - } - ] - } - } -) - -const transport = new NodeStreamableHTTPServerTransport({ sessionIdGenerator: undefined }); - -server.connect(transport); - +/** + * Create MCP router + * + * @param factories - context factories for database access + * @returns Express router with MCP integration endpoints + */ export const createMCPRouter = (factories: ContextFactories): express.Router => { const router = express.Router(); + const server = createMCPServer(factories); + /** + * POST /integration/mcp + * Initiate MCP integration connection + */ router.post("/", async (req, res, next) => { try { + /** + * Each request must use its own transport + */ + const transport = new NodeStreamableHTTPServerTransport({ sessionIdGenerator: undefined }); + await server.connect(transport); + await transport.handleRequest(req, res, req.body); } catch (error) { next(error); @@ -44,3 +33,36 @@ export const createMCPRouter = (factories: ContextFactories): express.Router => return router; }; + +/** + * Create MCP server + * + * @param factories - context factories for database access + * @returns configured MCP server + */ +const createMCPServer = (factories: ContextFactories) => { + const server = new McpServer({ + name: "server", + version: "0.0.0", + description: "A test server" + }); + + server.registerTool( + "hello_world", + { + description: "A test tool to greet the user" + }, + async () => { + return { + content: [ + { + type: "text" as const, + text: "Hi" + } + ] + } + } + ); + + return server; +}; From 58cc8154043530309629178cddfbba97c28f9622 Mon Sep 17 00:00:00 2001 From: github-actions <41898282+github-actions[bot]@users.noreply.github.com> Date: Wed, 19 Aug 2026 17:38:19 +0000 Subject: [PATCH 4/5] Bump version up to 1.5.11 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index b08f3f67..325d6535 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "hawk.api", - "version": "1.5.10", + "version": "1.5.11", "main": "index.ts", "license": "BUSL-1.1", "scripts": { From 6814c54d45defdd06920cccbf277413e81f372b1 Mon Sep 17 00:00:00 2001 From: github-actions <41898282+github-actions[bot]@users.noreply.github.com> Date: Wed, 19 Aug 2026 17:43:19 +0000 Subject: [PATCH 5/5] Bump version up to 1.5.14 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 8acb1cac..9285e695 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "hawk.api", - "version": "1.5.13", + "version": "1.5.14", "main": "index.ts", "license": "BUSL-1.1", "scripts": {