diff --git a/package-lock.json b/package-lock.json index 781f2ba..c949d68 100644 --- a/package-lock.json +++ b/package-lock.json @@ -10,7 +10,7 @@ "license": "Apache-2.0", "dependencies": { "@modelcontextprotocol/sdk": "^1.8.0", - "heroku": "10.4.1-alpha.3", + "heroku": "10.4.1-alpha.4", "jsonschema": "^1.5.0", "tar-stream": "^3.1.7", "zod": "^3.24.2", @@ -11276,9 +11276,9 @@ "license": "MIT" }, "node_modules/heroku": { - "version": "10.4.1-alpha.3", - "resolved": "https://registry.npmjs.org/heroku/-/heroku-10.4.1-alpha.3.tgz", - "integrity": "sha512-DGpZAD66ENRAoJRJSD2EaHGdFSZwH30jM0yD+/a0br1SNsRpZGo2/VvnwwxirFvJWiaGGrrAC4kDJ7W0ksb6dw==", + "version": "10.4.1-alpha.4", + "resolved": "https://registry.npmjs.org/heroku/-/heroku-10.4.1-alpha.4.tgz", + "integrity": "sha512-lqPS6t3tA/l+G9WKvdhHY2V2rVm3k90yOQaVj/CSNYjcYC8Kgjfdn9Gg2DEF880sqR3weJjdK/jwOpsSNPQ6FQ==", "license": "ISC", "dependencies": { "@heroku-cli/color": "2.0.1", diff --git a/package.json b/package.json index 935b9a1..d4bb5a5 100644 --- a/package.json +++ b/package.json @@ -10,7 +10,7 @@ "type": "module", "dependencies": { "@modelcontextprotocol/sdk": "^1.8.0", - "heroku": "10.4.1-alpha.3", + "heroku": "10.4.1-alpha.4", "jsonschema": "^1.5.0", "tar-stream": "^3.1.7", "zod": "^3.24.2", diff --git a/src/repl/heroku-cli-repl.spec.ts b/src/repl/heroku-cli-repl.spec.ts index e7259cb..29be1d2 100644 --- a/src/repl/heroku-cli-repl.spec.ts +++ b/src/repl/heroku-cli-repl.spec.ts @@ -1,8 +1,10 @@ import { EventEmitter } from 'node:events'; -import * as childProcess from 'node:child_process'; import { expect } from 'chai'; import sinon from 'sinon'; import { HerokuREPL } from './heroku-cli-repl.js'; +import * as pjson from '../../package.json' with { type: 'json' }; + +const VERSION = pjson.default.version; describe('HerokuREPL', () => { let repl: HerokuREPL; @@ -48,6 +50,12 @@ describe('HerokuREPL', () => { it('should create a new process when initialized', () => { expect(spawnStub.calledOnce).to.be.true; expect(spawnStub.firstCall.args[2].env.HEROKU_MCP_MODE).to.equal('true'); + expect(spawnStub.firstCall.args[2].env.HEROKU_MCP_SERVER_VERSION).to.equal(VERSION); + expect(spawnStub.firstCall.args[2].env.HEROKU_HEADERS).to.equal( + JSON.stringify({ + 'User-Agent': `Heroku-MCP-Server/${VERSION} (${process.platform}; ${process.arch}; node/${process.version})` + }) + ); }); }); diff --git a/src/repl/heroku-cli-repl.ts b/src/repl/heroku-cli-repl.ts index 448f0fa..d67a6e0 100644 --- a/src/repl/heroku-cli-repl.ts +++ b/src/repl/heroku-cli-repl.ts @@ -1,6 +1,7 @@ import { spawn, type ChildProcess } from 'node:child_process'; import path from 'node:path'; import { fileURLToPath } from 'node:url'; +import * as pjson from '../../package.json' with { type: 'json' }; const heroku = path.parse(path.relative(process.cwd(), fileURLToPath(import.meta.resolve('heroku', import.meta.url)))); const herokuRunPath = path.join(heroku.dir, '../', 'bin/run'); @@ -8,6 +9,8 @@ const herokuRunPath = path.join(heroku.dir, '../', 'bin/run'); type CommandQueueItem = { command: string; promise: Promise; resolver: (value: string) => void }; const COMMAND_END_RESULTS_MESSAGE = '<<>>'; const READY_MESSAGE = 'heroku >'; +const VERSION = pjson.default.version; + /** * A class that manages a Heroku CLI REPL process * and allows for durably executing commands via a queue. @@ -41,6 +44,7 @@ export class HerokuREPL { private pauseIteratorResolver: (() => void) | undefined; private readonly commandTimeout: number; private commandTimeoutId: NodeJS.Timeout | undefined; + private readonly userAgent: string = `Heroku-MCP-Server/${VERSION} (${process.platform}; ${process.arch}; node/${process.version})`; /** * Create a new HerokuREPL instance @@ -139,7 +143,11 @@ export class HerokuREPL { signal: this.abortController.signal, env: { ...process.env, - HEROKU_MCP_MODE: 'true' + HEROKU_MCP_MODE: 'true', + HEROKU_MCP_SERVER_VERSION: VERSION, + HEROKU_HEADERS: JSON.stringify({ + 'User-Agent': this.userAgent + }) } });