From 23c18154ed0d5fc74a2c83d1e809040f8e8fa23c Mon Sep 17 00:00:00 2001 From: David Blass Date: Tue, 2 Dec 2025 17:59:13 -0500 Subject: [PATCH] improve mcp context initialization --- entry | 61 +++++++++++++---------------------------------- main.ts | 15 ++---------- mcp/selectMode.ts | 31 ++++-------------------- mcp/server.ts | 8 +++++-- mcp/shared.ts | 48 ++++++++++++++++++------------------- package.json | 2 +- 6 files changed, 54 insertions(+), 111 deletions(-) diff --git a/entry b/entry index f52db4c..5a4d59b 100755 --- a/entry +++ b/entry @@ -83859,7 +83859,7 @@ function query({ // package.json var package_default = { name: "@pullfrog/action", - version: "0.0.114", + version: "0.0.115", type: "module", files: [ "index.js", @@ -121510,26 +121510,20 @@ var Octokit2 = Octokit.plugin(requestLog, legacyRestEndpointMethods, paginateRes ); // mcp/shared.ts -function getPayload() { - const payloadEnv = process.env.PULLFROG_PAYLOAD; - if (!payloadEnv) { - throw new Error("PULLFROG_PAYLOAD environment variable is required"); - } - try { - return JSON.parse(payloadEnv); - } catch (error41) { - throw new Error( - `Failed to parse PULLFROG_PAYLOAD: ${error41 instanceof Error ? error41.message : String(error41)}` - ); - } +var mcpInitContext; +function initMcpContext(state) { + mcpInitContext = state; } function getMcpContext() { + if (!mcpInitContext) { + throw new Error("MCP context not initialized. Call initializeMcpContext first."); + } return { + ...mcpInitContext, ...parseRepoContext(), octokit: new Octokit2({ auth: getGitHubInstallationToken() - }), - payload: getPayload() + }) }; } var tool = (toolDef) => toolDef; @@ -122299,17 +122293,6 @@ var ListPullRequestReviewsTool = tool({ }); // mcp/selectMode.ts -function getModes() { - const modesJson = process.env.PULLFROG_MODES; - if (modesJson) { - try { - return JSON.parse(modesJson); - } catch { - return []; - } - } - return []; -} var SelectMode = type({ modeName: type.string.describe( "the name of the mode to select (e.g., 'Plan', 'Build', 'Review', 'Prompt')" @@ -122319,19 +122302,13 @@ var SelectModeTool = tool({ name: "select_mode", description: "Select a mode and get its detailed prompt instructions. Call this first to determine which mode to use based on the request.", parameters: SelectMode, - execute: contextualize(async ({ modeName }) => { - const allModes = getModes(); - if (allModes.length === 0) { - return { - error: "No modes available. Modes must be provided via PULLFROG_MODES environment variable." - }; - } - const selectedMode = allModes.find((m) => m.name.toLowerCase() === modeName.toLowerCase()); + execute: contextualize(async ({ modeName }, ctx) => { + const selectedMode = ctx.modes.find((m) => m.name.toLowerCase() === modeName.toLowerCase()); if (!selectedMode) { - const availableModes = allModes.map((m) => m.name).join(", "); + const availableModes = ctx.modes.map((m) => m.name).join(", "); return { error: `Mode "${modeName}" not found. Available modes: ${availableModes}`, - availableModes: allModes.map((m) => ({ name: m.name, description: m.description })) + availableModes: ctx.modes.map((m) => ({ name: m.name, description: m.description })) }; } return { @@ -122367,7 +122344,8 @@ async function findAvailablePort(startPort) { } throw new Error(`Could not find available port starting from ${startPort}`); } -async function startMcpHttpServer() { +async function startMcpHttpServer(state) { + initMcpContext(state); const server = new FastMCP({ name: ghPullfrogMcpName, version: "0.0.1" @@ -122711,12 +122689,6 @@ function parsePayload(inputs) { } } async function startMcpServer(ctx) { - const repoContext = parseRepoContext(); - const githubRepository = `${repoContext.owner}/${repoContext.name}`; - const allModes = [...modes, ...ctx.payload.modes || []]; - process.env.GITHUB_REPOSITORY = githubRepository; - process.env.PULLFROG_MODES = JSON.stringify(allModes); - process.env.PULLFROG_PAYLOAD = JSON.stringify(ctx.payload); const runId = process.env.GITHUB_RUN_ID; if (runId) { const workflowRunInfo = await fetchWorkflowRunInfo(runId); @@ -122725,7 +122697,8 @@ async function startMcpServer(ctx) { log.info(`\u{1F4DD} Using pre-created progress comment: ${workflowRunInfo.progressCommentId}`); } } - const { url: url2, close } = await startMcpHttpServer(); + const allModes = [...modes, ...ctx.payload.modes || []]; + const { url: url2, close } = await startMcpHttpServer({ payload: ctx.payload, modes: allModes }); ctx.mcpServerUrl = url2; ctx.mcpServerClose = close; log.info(`\u{1F680} MCP server started at ${url2}`); diff --git a/main.ts b/main.ts index d0e327c..19626e2 100644 --- a/main.ts +++ b/main.ts @@ -271,17 +271,6 @@ function parsePayload(inputs: Inputs): Payload { } async function startMcpServer(ctx: MainContext): Promise { - // set environment variables for MCP server tools - const repoContext = parseRepoContext(); - const githubRepository = `${repoContext.owner}/${repoContext.name}`; - const allModes = [...modes, ...(ctx.payload.modes || [])]; - - process.env.GITHUB_REPOSITORY = githubRepository; - process.env.PULLFROG_MODES = JSON.stringify(allModes); - process.env.PULLFROG_PAYLOAD = JSON.stringify(ctx.payload); - - // GITHUB_RUN_ID is already set in GitHub Actions, no need to set it here - // fetch the pre-created progress comment ID from the database // this must be set BEFORE starting the MCP server so comment.ts can read it const runId = process.env.GITHUB_RUN_ID; @@ -293,8 +282,8 @@ async function startMcpServer(ctx: MainContext): Promise { } } - // start MCP server after env vars are set (comment.ts reads PULLFROG_PROGRESS_COMMENT_ID at import time) - const { url, close } = await startMcpHttpServer(); + const allModes = [...modes, ...(ctx.payload.modes || [])]; + const { url, close } = await startMcpHttpServer({ payload: ctx.payload, modes: allModes }); ctx.mcpServerUrl = url; ctx.mcpServerClose = close; log.info(`🚀 MCP server started at ${url}`); diff --git a/mcp/selectMode.ts b/mcp/selectMode.ts index 4f193a5..9c8860f 100644 --- a/mcp/selectMode.ts +++ b/mcp/selectMode.ts @@ -1,20 +1,6 @@ import { type } from "arktype"; -import type { Mode } from "../modes.ts"; import { contextualize, tool } from "./shared.ts"; -// Get modes from environment variable (set by createMcpConfigs) -function getModes(): Mode[] { - const modesJson = process.env.PULLFROG_MODES; - if (modesJson) { - try { - return JSON.parse(modesJson); - } catch { - return []; - } - } - return []; -} - export const SelectMode = type({ modeName: type.string.describe( "the name of the mode to select (e.g., 'Plan', 'Build', 'Review', 'Prompt')" @@ -26,23 +12,14 @@ export const SelectModeTool = tool({ description: "Select a mode and get its detailed prompt instructions. Call this first to determine which mode to use based on the request.", parameters: SelectMode, - execute: contextualize(async ({ modeName }) => { - const allModes = getModes(); - - if (allModes.length === 0) { - return { - error: - "No modes available. Modes must be provided via PULLFROG_MODES environment variable.", - }; - } - - const selectedMode = allModes.find((m) => m.name.toLowerCase() === modeName.toLowerCase()); + execute: contextualize(async ({ modeName }, ctx) => { + const selectedMode = ctx.modes.find((m) => m.name.toLowerCase() === modeName.toLowerCase()); if (!selectedMode) { - const availableModes = allModes.map((m) => m.name).join(", "); + const availableModes = ctx.modes.map((m) => m.name).join(", "); return { error: `Mode "${modeName}" not found. Available modes: ${availableModes}`, - availableModes: allModes.map((m) => ({ name: m.name, description: m.description })), + availableModes: ctx.modes.map((m) => ({ name: m.name, description: m.description })), }; } diff --git a/mcp/server.ts b/mcp/server.ts index 259dd4e..5419f40 100644 --- a/mcp/server.ts +++ b/mcp/server.ts @@ -20,7 +20,7 @@ import { PullRequestInfoTool } from "./prInfo.ts"; import { ReviewTool } from "./review.ts"; import { GetReviewCommentsTool, ListPullRequestReviewsTool } from "./reviewComments.ts"; import { SelectModeTool } from "./selectMode.ts"; -import { addTools } from "./shared.ts"; +import { addTools, initMcpContext, type McpInitContext } from "./shared.ts"; /** * Find an available port starting from the given port @@ -54,7 +54,11 @@ async function findAvailablePort(startPort: number): Promise { /** * Start the MCP HTTP server and return the URL and close function */ -export async function startMcpHttpServer(): Promise<{ url: string; close: () => Promise }> { +export async function startMcpHttpServer( + state: McpInitContext +): Promise<{ url: string; close: () => Promise }> { + initMcpContext(state); + const server = new FastMCP({ name: ghPullfrogMcpName, version: "0.0.1", diff --git a/mcp/shared.ts b/mcp/shared.ts index c6bb750..fda0b67 100644 --- a/mcp/shared.ts +++ b/mcp/shared.ts @@ -2,46 +2,38 @@ import { Octokit } from "@octokit/rest"; import type { StandardSchemaV1 } from "@standard-schema/spec"; import type { FastMCP, Tool } from "fastmcp"; import type { Payload } from "../external.ts"; +import type { Mode } from "../modes.ts"; import { getGitHubInstallationToken, parseRepoContext, type RepoContext } from "../utils/github.ts"; -export interface ToolResult { - content: { - type: "text"; - text: string; - }[]; - isError?: boolean; +export interface McpInitContext { + payload: Payload; + modes: Mode[]; } -export function getPayload(): Payload { - const payloadEnv = process.env.PULLFROG_PAYLOAD; - if (!payloadEnv) { - throw new Error("PULLFROG_PAYLOAD environment variable is required"); - } +let mcpInitContext: McpInitContext | undefined; - try { - return JSON.parse(payloadEnv) as Payload; - } catch (error) { - throw new Error( - `Failed to parse PULLFROG_PAYLOAD: ${error instanceof Error ? error.message : String(error)}` - ); - } +// this must be called on mcp server initialization +export function initMcpContext(state: McpInitContext): void { + mcpInitContext = state; +} + +export interface McpContext extends McpInitContext, RepoContext { + octokit: Octokit; } export function getMcpContext(): McpContext { + if (!mcpInitContext) { + throw new Error("MCP context not initialized. Call initializeMcpContext first."); + } return { + ...mcpInitContext, ...parseRepoContext(), octokit: new Octokit({ auth: getGitHubInstallationToken(), }), - payload: getPayload(), }; } -export interface McpContext extends RepoContext { - octokit: Octokit; - payload: Payload; -} - export const tool = (toolDef: Tool>) => toolDef; export const addTools = (server: FastMCP, tools: Tool[]) => { @@ -65,6 +57,14 @@ export const contextualize = ( }; }; +export interface ToolResult { + content: { + type: "text"; + text: string; + }[]; + isError?: boolean; +} + const handleToolSuccess = (data: Record): ToolResult => { return { content: [ diff --git a/package.json b/package.json index 6804689..250fb7b 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@pullfrog/action", - "version": "0.0.114", + "version": "0.0.115", "type": "module", "files": [ "index.js",