Compare commits

...

1 Commits

Author SHA1 Message Date
David Blass 5a21d40d27 start mcp server in memory 2025-11-06 17:56:06 -05:00
7 changed files with 357 additions and 172 deletions
-39
View File
@@ -1,39 +0,0 @@
#!/usr/bin/env node
/**
* Build entry point - wraps entry.ts to avoid top-level await in CJS output
*/
import * as core from "@actions/core";
import { type } from "arktype";
import { Inputs, main } from "./main.ts";
import packageJson from "./package.json" with { type: "json" };
import { log } from "./utils/cli.ts";
async function run(): Promise<void> {
try {
log.info(`🐸 Running pullfrog/action@${packageJson.version}...`);
const inputsJson = process.env.INPUTS_JSON;
if (!inputsJson) {
throw new Error("INPUTS_JSON environment variable not found");
}
const parsed = type("string.json.parse").assert(inputsJson);
const inputs = Inputs.assert(parsed);
const result = await main(inputs);
if (!result.success) {
throw new Error(result.error || "Agent execution failed");
}
} catch (error) {
const errorMessage = error instanceof Error ? error.message : "Unknown error occurred";
core.setFailed(`Action failed: ${errorMessage}`);
}
}
// Wrap in IIFE to avoid top-level await in CJS
(async () => {
await run();
})();
+316 -110
View File
File diff suppressed because one or more lines are too long
+12 -1
View File
@@ -7,9 +7,13 @@
import * as core from "@actions/core"; import * as core from "@actions/core";
import { type } from "arktype"; import { type } from "arktype";
import { Inputs, main } from "./main.ts"; import { Inputs, main } from "./main.ts";
import { createMcpServer } from "./mcp/server.ts";
import packageJson from "./package.json" with { type: "json" }; import packageJson from "./package.json" with { type: "json" };
import { log } from "./utils/cli.ts"; import { log } from "./utils/cli.ts";
// Export createMcpServer so it can be called from the spawned MCP process
export { createMcpServer };
async function run(): Promise<void> { async function run(): Promise<void> {
try { try {
log.info(`🐸 Running pullfrog/action@${packageJson.version}...`); log.info(`🐸 Running pullfrog/action@${packageJson.version}...`);
@@ -33,4 +37,11 @@ async function run(): Promise<void> {
} }
} }
await run(); // Only run main action if INPUTS_JSON is set (running as GitHub Action)
// When SDK spawns MCP server, it calls createMcpServer() directly
if (process.env.INPUTS_JSON) {
// Wrap in IIFE to avoid top-level await in CJS
(async () => {
await run();
})();
}
+7 -4
View File
@@ -3,7 +3,7 @@ import { build } from "esbuild";
// Build the GitHub Action bundle only // Build the GitHub Action bundle only
// For npm package builds, use zshy (pnpm build:npm) // For npm package builds, use zshy (pnpm build:npm)
await build({ await build({
entryPoints: ["./entry.build.ts"], entryPoints: ["./entry.ts"],
bundle: true, bundle: true,
outfile: "./entry.cjs", outfile: "./entry.cjs",
format: "cjs", format: "cjs",
@@ -11,9 +11,12 @@ await build({
target: "node20", target: "node20",
minify: true, minify: true,
sourcemap: false, sourcemap: false,
// @actions/core is provided by GitHub Actions runtime, but we still need it bundled // Mark optional peer dependencies as external to avoid bundling errors
// for local testing. However, we can mark it to reduce duplication if needed. external: [
external: [], "@valibot/to-json-schema",
"effect",
"sury",
],
// Enable tree-shaking to remove unused code // Enable tree-shaking to remove unused code
treeShaking: true, treeShaking: true,
// Drop console statements in production (but keep for debugging) // Drop console statements in production (but keep for debugging)
+6 -4
View File
@@ -3,11 +3,8 @@
*/ */
import type { McpServerConfig } from "@anthropic-ai/claude-agent-sdk"; import type { McpServerConfig } from "@anthropic-ai/claude-agent-sdk";
import { fromHere } from "@ark/fs";
import { parseRepoContext } from "../utils/github.ts"; import { parseRepoContext } from "../utils/github.ts";
const actionPath = fromHere("..");
export const ghPullfrogMcpName = "gh-pullfrog"; export const ghPullfrogMcpName = "gh-pullfrog";
export type McpName = typeof ghPullfrogMcpName; export type McpName = typeof ghPullfrogMcpName;
@@ -18,10 +15,15 @@ export function createMcpConfigs(githubInstallationToken: string): McpConfigs {
const repoContext = parseRepoContext(); const repoContext = parseRepoContext();
const githubRepository = `${repoContext.owner}/${repoContext.name}`; const githubRepository = `${repoContext.owner}/${repoContext.name}`;
// Get absolute path to entry.cjs - use GITHUB_ACTION_PATH if available, otherwise current directory
const entryPath = process.env.GITHUB_ACTION_PATH
? `${process.env.GITHUB_ACTION_PATH}/entry.cjs`
: `${process.cwd()}/entry.cjs`;
return { return {
[ghPullfrogMcpName]: { [ghPullfrogMcpName]: {
command: "node", command: "node",
args: [`${actionPath}/mcp/server.ts`], args: ["-e", `require('${entryPath.replace(/'/g, "\\'")}').createMcpServer()`],
env: { env: {
GITHUB_INSTALLATION_TOKEN: githubInstallationToken, GITHUB_INSTALLATION_TOKEN: githubInstallationToken,
GITHUB_REPOSITORY: githubRepository, GITHUB_REPOSITORY: githubRepository,
+15 -13
View File
@@ -8,18 +8,20 @@ import { PullRequestInfoTool } from "./prInfo.ts";
import { ReviewTool } from "./review.ts"; import { ReviewTool } from "./review.ts";
import { addTools } from "./shared.ts"; import { addTools } from "./shared.ts";
const server = new FastMCP({ export function createMcpServer(): void {
name: "gh-pullfrog", const server = new FastMCP({
version: "0.0.1", name: "gh-pullfrog",
}); version: "0.0.1",
});
addTools(server, [ addTools(server, [
CreateCommentTool, CreateCommentTool,
EditCommentTool, EditCommentTool,
IssueTool, IssueTool,
PullRequestTool, PullRequestTool,
ReviewTool, ReviewTool,
PullRequestInfoTool, PullRequestInfoTool,
]); ]);
server.start(); server.start();
}
+1 -1
View File
@@ -1,6 +1,6 @@
{ {
"name": "@pullfrog/action", "name": "@pullfrog/action",
"version": "0.0.74", "version": "0.0.75",
"type": "module", "type": "module",
"files": [ "files": [
"index.js", "index.js",