@papi-ai/server 0.7.7 → 0.7.8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.js +208 -20
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -7703,7 +7703,7 @@ ${r.content}` + (r.carry_forward ? `
|
|
|
7703
7703
|
FROM cycle_tasks
|
|
7704
7704
|
WHERE project_id = ${this.projectId}
|
|
7705
7705
|
AND status NOT IN ('Done', 'Cancelled', 'Archived', 'Deferred')
|
|
7706
|
-
AND (task_type IN ('task', 'bug', 'research') OR task_type IS NULL)
|
|
7706
|
+
AND (task_type IN ('task', 'bug', 'research', 'discovery', 'spike') OR task_type IS NULL)
|
|
7707
7707
|
ORDER BY
|
|
7708
7708
|
CASE priority
|
|
7709
7709
|
WHEN 'P0 Critical' THEN 0
|
|
@@ -9224,9 +9224,11 @@ var init_git = __esm({
|
|
|
9224
9224
|
|
|
9225
9225
|
// src/index.ts
|
|
9226
9226
|
import { readFileSync as readFileSync4 } from "fs";
|
|
9227
|
+
import { createServer as createHttpServer } from "http";
|
|
9227
9228
|
import { dirname as dirname2, join as join11 } from "path";
|
|
9228
9229
|
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
9229
9230
|
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
9231
|
+
import { StreamableHTTPServerTransport } from "@modelcontextprotocol/sdk/server/streamableHttp.js";
|
|
9230
9232
|
import { Server as Server2 } from "@modelcontextprotocol/sdk/server/index.js";
|
|
9231
9233
|
import {
|
|
9232
9234
|
CallToolRequestSchema as CallToolRequestSchema2,
|
|
@@ -9249,6 +9251,8 @@ function loadConfig() {
|
|
|
9249
9251
|
const baseBranch = process.env.PAPI_BASE_BRANCH ?? "main";
|
|
9250
9252
|
const autoPR = process.env.PAPI_AUTO_PR !== "false";
|
|
9251
9253
|
const lightMode = process.env.PAPI_LIGHT_MODE === "true";
|
|
9254
|
+
const projectOwner = process.env.PAPI_OWNER ?? "Cathal";
|
|
9255
|
+
const skipProjectSpecificRules = process.env.PAPI_SKIP_PROJECT_RULES === "true";
|
|
9252
9256
|
const papiEndpoint = process.env.PAPI_ENDPOINT;
|
|
9253
9257
|
const dataEndpoint = process.env.PAPI_DATA_ENDPOINT;
|
|
9254
9258
|
const databaseUrl = process.env.DATABASE_URL;
|
|
@@ -9263,7 +9267,9 @@ function loadConfig() {
|
|
|
9263
9267
|
autoPR,
|
|
9264
9268
|
adapterType,
|
|
9265
9269
|
papiEndpoint,
|
|
9266
|
-
lightMode
|
|
9270
|
+
lightMode,
|
|
9271
|
+
projectOwner,
|
|
9272
|
+
skipProjectSpecificRules
|
|
9267
9273
|
};
|
|
9268
9274
|
}
|
|
9269
9275
|
|
|
@@ -12083,10 +12089,11 @@ async function assembleContext(adapter2, mode, _config, filters, focus) {
|
|
|
12083
12089
|
timings["getPlanContextSummary"] = t();
|
|
12084
12090
|
if (leanSummary) {
|
|
12085
12091
|
t = startTimer();
|
|
12086
|
-
|
|
12092
|
+
const [metricsSnapshotsRaw, reviews2] = await Promise.all([
|
|
12087
12093
|
adapter2.readCycleMetrics(),
|
|
12088
12094
|
adapter2.getRecentReviews(5)
|
|
12089
12095
|
]);
|
|
12096
|
+
let metricsSnapshots2 = metricsSnapshotsRaw;
|
|
12090
12097
|
let leanBuildReports = [];
|
|
12091
12098
|
try {
|
|
12092
12099
|
leanBuildReports = await adapter2.getRecentBuildReports(50);
|
|
@@ -17205,7 +17212,7 @@ function extractDocMeta(absolutePath, relativePath, cycleNumber) {
|
|
|
17205
17212
|
let title = relativePath.split("/").pop()?.replace(".md", "") ?? relativePath;
|
|
17206
17213
|
let type = "reference";
|
|
17207
17214
|
let cycle = cycleNumber;
|
|
17208
|
-
|
|
17215
|
+
const summary = "Auto-registered \u2014 no summary available. Update via doc_register.";
|
|
17209
17216
|
if (relativePath.startsWith("docs/research/")) type = "research";
|
|
17210
17217
|
else if (relativePath.startsWith("docs/architecture/")) type = "architecture";
|
|
17211
17218
|
else if (relativePath.startsWith("docs/audits/")) type = "audit";
|
|
@@ -17612,8 +17619,6 @@ For M/L tasks: use the full toolchain \u2014 Playground (design preview) \u2192
|
|
|
17612
17619
|
- Check adapter-pg implementation, not adapter-md. adapter-md is legacy.
|
|
17613
17620
|
- Verify the full write\u2192DB\u2192read\u2192consumer path for any data changes.
|
|
17614
17621
|
- Run migrations on dev before prod. Test with \`execute_sql\` via Supabase MCP.
|
|
17615
|
-
- When adding adapter interface methods, implement in BOTH adapter-md and adapter-pg.
|
|
17616
|
-
- Build order matters: adapter-md \u2192 adapter-pg \u2192 server.
|
|
17617
17622
|
- .papi/ files may be stale \u2014 DB via MCP tools is the source of truth.`,
|
|
17618
17623
|
Auth: `**MODULE INSTRUCTIONS \u2014 Auth**
|
|
17619
17624
|
- NEVER expose the Supabase service role key in client-side code or API responses.
|
|
@@ -18366,15 +18371,15 @@ ${lines.join("\n")}
|
|
|
18366
18371
|
]);
|
|
18367
18372
|
warnIfEmpty("getCycleHealth (idea)", health);
|
|
18368
18373
|
const phase = input.phase || resolveCurrentPhase(phases);
|
|
18369
|
-
const
|
|
18374
|
+
const VALID_PRIORITIES3 = /* @__PURE__ */ new Set(["P0 Critical", "P1 High", "P2 Medium", "P3 Low"]);
|
|
18370
18375
|
const VALID_COMPLEXITIES2 = /* @__PURE__ */ new Set(["XS", "Small", "Medium", "Large", "XL"]);
|
|
18371
|
-
const priority = input.priority &&
|
|
18376
|
+
const priority = input.priority && VALID_PRIORITIES3.has(input.priority) ? input.priority : "P2 Medium";
|
|
18372
18377
|
const complexity = input.complexity && VALID_COMPLEXITIES2.has(input.complexity) ? input.complexity : "Small";
|
|
18373
|
-
const
|
|
18378
|
+
const VALID_TYPES2 = /* @__PURE__ */ new Set(["task", "bug", "research", "idea", "spike", "discovery"]);
|
|
18374
18379
|
let taskTitle = input.text;
|
|
18375
18380
|
let taskType = "idea";
|
|
18376
18381
|
let typeInferred = false;
|
|
18377
|
-
if (input.type &&
|
|
18382
|
+
if (input.type && VALID_TYPES2.has(input.type)) {
|
|
18378
18383
|
taskType = input.type;
|
|
18379
18384
|
} else {
|
|
18380
18385
|
const PREFIX_MAP = {
|
|
@@ -18843,16 +18848,16 @@ async function recordAdHoc(adapter2, input) {
|
|
|
18843
18848
|
displayId: "",
|
|
18844
18849
|
title: input.title,
|
|
18845
18850
|
status: "Done",
|
|
18846
|
-
priority: "
|
|
18851
|
+
priority: input.priority || "P2 Medium",
|
|
18847
18852
|
complexity: input.effort === "XS" || input.effort === "S" ? "Small" : "Medium",
|
|
18848
18853
|
module: input.module || "Core",
|
|
18849
18854
|
epic: input.epic || "Platform",
|
|
18850
18855
|
phase,
|
|
18851
|
-
owner: "Cathal",
|
|
18856
|
+
owner: input.owner || "Cathal",
|
|
18852
18857
|
reviewed: true,
|
|
18853
18858
|
createdCycle: cycle,
|
|
18854
18859
|
notes: input.notes ? `[ad-hoc] ${input.notes}` : "[ad-hoc]",
|
|
18855
|
-
taskType: "task",
|
|
18860
|
+
taskType: input.taskType || "task",
|
|
18856
18861
|
source: "owner"
|
|
18857
18862
|
});
|
|
18858
18863
|
}
|
|
@@ -18877,6 +18882,16 @@ async function recordAdHoc(adapter2, input) {
|
|
|
18877
18882
|
|
|
18878
18883
|
// src/tools/ad-hoc.ts
|
|
18879
18884
|
var VALID_EFFORTS = ["XS", "S", "M", "L", "XL"];
|
|
18885
|
+
var VALID_PRIORITIES = ["P0 Critical", "P1 High", "P2 Medium", "P3 Low"];
|
|
18886
|
+
var VALID_TYPES = ["task", "bug", "research", "discovery", "spike", "idea"];
|
|
18887
|
+
function inferTaskType(description) {
|
|
18888
|
+
const lower = description.toLowerCase();
|
|
18889
|
+
if (/\bfix\b|bug|error|crash|broken|regression|defect/.test(lower)) return "bug";
|
|
18890
|
+
if (/research|investigate|explore|analysis|audit|review|assess/.test(lower)) return "research";
|
|
18891
|
+
if (/discover|discovery|found|uncovered/.test(lower)) return "discovery";
|
|
18892
|
+
if (/spike|poc|proof.of.concept|prototype|experiment/.test(lower)) return "spike";
|
|
18893
|
+
return "task";
|
|
18894
|
+
}
|
|
18880
18895
|
var adHocTool = {
|
|
18881
18896
|
name: "ad_hoc",
|
|
18882
18897
|
description: "Record work done outside the normal cycle. Creates a Done task with a lightweight build report, or associates work with an existing task if task_id is provided (without changing task status \u2014 use build_execute for status transitions). Use for quick fixes, bug patches, or ad-hoc changes. Does not call the Anthropic API.",
|
|
@@ -18908,6 +18923,16 @@ var adHocTool = {
|
|
|
18908
18923
|
epic: {
|
|
18909
18924
|
type: "string",
|
|
18910
18925
|
description: 'Epic this relates to (default: "Platform").'
|
|
18926
|
+
},
|
|
18927
|
+
priority: {
|
|
18928
|
+
type: "string",
|
|
18929
|
+
enum: ["P0 Critical", "P1 High", "P2 Medium", "P3 Low"],
|
|
18930
|
+
description: "Task priority (default: P2 Medium). Use P1 for important fixes, P0 for critical incidents."
|
|
18931
|
+
},
|
|
18932
|
+
type: {
|
|
18933
|
+
type: "string",
|
|
18934
|
+
enum: ["task", "bug", "research", "discovery", "spike", "idea"],
|
|
18935
|
+
description: 'Task type (default: inferred from description \u2014 "fix"/"bug" \u2192 bug, "research"/"investigate" \u2192 research, otherwise task).'
|
|
18911
18936
|
}
|
|
18912
18937
|
},
|
|
18913
18938
|
required: []
|
|
@@ -18923,6 +18948,14 @@ async function handleAdHoc(adapter2, config2, args) {
|
|
|
18923
18948
|
if (!VALID_EFFORTS.includes(effortRaw)) {
|
|
18924
18949
|
return errorResponse(`effort must be one of: ${VALID_EFFORTS.join(", ")}`);
|
|
18925
18950
|
}
|
|
18951
|
+
const priorityRaw = args.priority || "P2 Medium";
|
|
18952
|
+
if (!VALID_PRIORITIES.includes(priorityRaw)) {
|
|
18953
|
+
return errorResponse(`priority must be one of: ${VALID_PRIORITIES.join(", ")}`);
|
|
18954
|
+
}
|
|
18955
|
+
const typeRaw = args.type || inferTaskType(title || (args.notes ?? ""));
|
|
18956
|
+
if (!VALID_TYPES.includes(typeRaw)) {
|
|
18957
|
+
return errorResponse(`type must be one of: ${VALID_TYPES.join(", ")}`);
|
|
18958
|
+
}
|
|
18926
18959
|
const MAX_NOTES_LENGTH = 2e3;
|
|
18927
18960
|
let rawNotes = args.notes?.trim();
|
|
18928
18961
|
let notesTruncated = false;
|
|
@@ -18936,7 +18969,11 @@ async function handleAdHoc(adapter2, config2, args) {
|
|
|
18936
18969
|
notes: rawNotes,
|
|
18937
18970
|
effort: effortRaw,
|
|
18938
18971
|
module: args.module,
|
|
18939
|
-
epic: args.epic
|
|
18972
|
+
epic: args.epic,
|
|
18973
|
+
priority: priorityRaw,
|
|
18974
|
+
taskType: typeRaw,
|
|
18975
|
+
// PROJECT-SPECIFIC: owner resolved from config (PAPI_OWNER env var, default 'Cathal')
|
|
18976
|
+
owner: config2.projectOwner
|
|
18940
18977
|
});
|
|
18941
18978
|
if (isGitAvailable() && isGitRepo(config2.projectRoot)) {
|
|
18942
18979
|
try {
|
|
@@ -18949,8 +18986,11 @@ async function handleAdHoc(adapter2, config2, args) {
|
|
|
18949
18986
|
}
|
|
18950
18987
|
}
|
|
18951
18988
|
const truncateWarning = notesTruncated ? ` (notes truncated to ${MAX_NOTES_LENGTH} chars)` : "";
|
|
18989
|
+
const taskModule = result.task.module || "Core";
|
|
18990
|
+
const typeLabel = result.task.taskType || typeRaw;
|
|
18952
18991
|
return textResponse(
|
|
18953
|
-
`**${result.task.id}:** "${result.task.title}"
|
|
18992
|
+
`**${result.task.id}:** "${result.task.title}" recorded (${effortRaw}, ${priorityRaw}, ${typeLabel}, ${taskModule}).${truncateWarning} Build report attached.
|
|
18993
|
+
_To correct: board_edit ${result.task.id} with updated fields._`
|
|
18954
18994
|
);
|
|
18955
18995
|
}
|
|
18956
18996
|
|
|
@@ -19173,7 +19213,7 @@ async function applyReconcile(adapter2, corrections) {
|
|
|
19173
19213
|
}
|
|
19174
19214
|
return { applied, skipped, details, phaseChanges };
|
|
19175
19215
|
}
|
|
19176
|
-
var
|
|
19216
|
+
var VALID_PRIORITIES2 = /* @__PURE__ */ new Set(["P0 Critical", "P1 High", "P2 Medium", "P3 Low"]);
|
|
19177
19217
|
var VALID_COMPLEXITIES = /* @__PURE__ */ new Set(["XS", "Small", "Medium", "Large", "XL"]);
|
|
19178
19218
|
async function prepareRetriage(adapter2) {
|
|
19179
19219
|
const health = await adapter2.getCycleHealth();
|
|
@@ -19234,7 +19274,7 @@ async function applyRetriage(adapter2, retriages) {
|
|
|
19234
19274
|
skipped++;
|
|
19235
19275
|
continue;
|
|
19236
19276
|
}
|
|
19237
|
-
if (!
|
|
19277
|
+
if (!VALID_PRIORITIES2.has(r.priority)) {
|
|
19238
19278
|
details.push(`${r.taskId}: skipped \u2014 invalid priority "${r.priority}"`);
|
|
19239
19279
|
skipped++;
|
|
19240
19280
|
continue;
|
|
@@ -20205,11 +20245,41 @@ ${result.handoffRegenPrompt.userMessage}
|
|
|
20205
20245
|
}
|
|
20206
20246
|
}
|
|
20207
20247
|
let autoReleaseNote = "";
|
|
20248
|
+
let batchSummaryNote = "";
|
|
20208
20249
|
if (stage === "build-acceptance" && verdict === "accept" && result.newStatus === "Done" && result.currentCycle > 0) {
|
|
20209
20250
|
try {
|
|
20210
20251
|
const allTasks = await adapter2.queryBoard();
|
|
20211
20252
|
const cycleTasks = allTasks.filter((t) => t.cycle === result.currentCycle);
|
|
20212
20253
|
if (cycleTasks.length > 0 && cycleTasks.every((t) => t.status === "Done")) {
|
|
20254
|
+
try {
|
|
20255
|
+
const allReviews = await adapter2.getRecentReviews(200);
|
|
20256
|
+
const cycleReviews = allReviews.filter(
|
|
20257
|
+
(r) => r.cycle === result.currentCycle && r.stage === "build-acceptance"
|
|
20258
|
+
);
|
|
20259
|
+
const reviewsWithAutoReview = cycleReviews.filter((r) => r.autoReview);
|
|
20260
|
+
if (reviewsWithAutoReview.length > 0) {
|
|
20261
|
+
const verdictCounts = { pass: 0, warn: 0, fail: 0 };
|
|
20262
|
+
const findingsBySeverity = { error: 0, warning: 0, info: 0 };
|
|
20263
|
+
for (const r of reviewsWithAutoReview) {
|
|
20264
|
+
if (r.autoReview) {
|
|
20265
|
+
verdictCounts[r.autoReview.verdict] = (verdictCounts[r.autoReview.verdict] ?? 0) + 1;
|
|
20266
|
+
for (const f of r.autoReview.findings) {
|
|
20267
|
+
findingsBySeverity[f.severity] = (findingsBySeverity[f.severity] ?? 0) + 1;
|
|
20268
|
+
}
|
|
20269
|
+
}
|
|
20270
|
+
}
|
|
20271
|
+
const totalFindings = findingsBySeverity.error + findingsBySeverity.warning + findingsBySeverity.info;
|
|
20272
|
+
batchSummaryNote = `
|
|
20273
|
+
|
|
20274
|
+
---
|
|
20275
|
+
|
|
20276
|
+
**Cycle ${result.currentCycle} Auto-Review Summary** (${reviewsWithAutoReview.length}/${cycleReviews.length} reviews had auto-review)
|
|
20277
|
+
|
|
20278
|
+
- Verdicts: ${verdictCounts.pass} pass, ${verdictCounts.warn} warn, ${verdictCounts.fail} fail
|
|
20279
|
+
` + (totalFindings > 0 ? `- Findings: ${findingsBySeverity.error} error${findingsBySeverity.error !== 1 ? "s" : ""}, ${findingsBySeverity.warning} warning${findingsBySeverity.warning !== 1 ? "s" : ""}, ${findingsBySeverity.info} info` : "- No findings logged");
|
|
20280
|
+
}
|
|
20281
|
+
} catch {
|
|
20282
|
+
}
|
|
20213
20283
|
const version = `v0.${result.currentCycle}.0`;
|
|
20214
20284
|
const baseBranch = resolveBaseBranch(config2.projectRoot, config2.baseBranch);
|
|
20215
20285
|
const releaseResult = await createRelease(config2, baseBranch, version, adapter2);
|
|
@@ -20254,7 +20324,7 @@ Next: address the feedback, then run \`build_execute ${taskId}\` to resubmit.`;
|
|
|
20254
20324
|
- **Verdict:** ${result.verdict}
|
|
20255
20325
|
- **Comments:** ${result.comments}
|
|
20256
20326
|
|
|
20257
|
-
${statusNote}${autoReviewNote}${unblockNote}${regenNote}${mergeNote}${autoReleaseNote}${nextStepNote}${phaseNote}`
|
|
20327
|
+
${statusNote}${autoReviewNote}${unblockNote}${regenNote}${mergeNote}${batchSummaryNote}${autoReleaseNote}${nextStepNote}${phaseNote}`
|
|
20258
20328
|
);
|
|
20259
20329
|
} catch (err) {
|
|
20260
20330
|
return errorResponse(err instanceof Error ? err.message : String(err));
|
|
@@ -22453,5 +22523,123 @@ if (pkgVersion !== "unknown") {
|
|
|
22453
22523
|
}
|
|
22454
22524
|
})();
|
|
22455
22525
|
}
|
|
22456
|
-
var
|
|
22457
|
-
|
|
22526
|
+
var httpPortRaw = process.env["PAPI_HTTP_PORT"] ?? process.env["PORT"];
|
|
22527
|
+
var httpPort = httpPortRaw ? parseInt(httpPortRaw, 10) : void 0;
|
|
22528
|
+
var httpHost = process.env["PORT"] ? "0.0.0.0" : "127.0.0.1";
|
|
22529
|
+
if (httpPort) {
|
|
22530
|
+
if (isNaN(httpPort) || httpPort < 1 || httpPort > 65535) {
|
|
22531
|
+
process.stderr.write(`[papi] Invalid PAPI_HTTP_PORT: "${process.env.PAPI_HTTP_PORT}". Must be a number between 1 and 65535.
|
|
22532
|
+
`);
|
|
22533
|
+
process.exit(1);
|
|
22534
|
+
}
|
|
22535
|
+
const httpToken = process.env.PAPI_HTTP_TOKEN;
|
|
22536
|
+
if (!httpToken) {
|
|
22537
|
+
process.stderr.write("[papi] WARNING: PAPI_HTTP_TOKEN is not set. HTTP transport is unauthenticated \u2014 anyone with the URL can call your PAPI tools. Set PAPI_HTTP_TOKEN to a secret string.\n");
|
|
22538
|
+
}
|
|
22539
|
+
const createServerForRequest = () => {
|
|
22540
|
+
if (adapter && !setupError) {
|
|
22541
|
+
return createServer(adapter, config);
|
|
22542
|
+
}
|
|
22543
|
+
const errorServer = new Server2(
|
|
22544
|
+
{ name: "papi", version: pkgVersion },
|
|
22545
|
+
{ capabilities: { tools: {} } }
|
|
22546
|
+
);
|
|
22547
|
+
const errorMessage = setupError || "Unknown startup error";
|
|
22548
|
+
errorServer.setRequestHandler(ListToolsRequestSchema2, async () => ({
|
|
22549
|
+
tools: [{
|
|
22550
|
+
name: "setup",
|
|
22551
|
+
description: "PAPI is not connected \u2014 run this tool for setup instructions.",
|
|
22552
|
+
inputSchema: { type: "object", properties: {}, required: [] }
|
|
22553
|
+
}]
|
|
22554
|
+
}));
|
|
22555
|
+
errorServer.setRequestHandler(CallToolRequestSchema2, async () => ({
|
|
22556
|
+
content: [{
|
|
22557
|
+
type: "text",
|
|
22558
|
+
text: `# PAPI Connection Error
|
|
22559
|
+
|
|
22560
|
+
${errorMessage}
|
|
22561
|
+
|
|
22562
|
+
## Quick Fix
|
|
22563
|
+
|
|
22564
|
+
If you haven't set up PAPI yet:
|
|
22565
|
+
1. Go to https://getpapi.ai/login and sign up
|
|
22566
|
+
2. Complete the onboarding wizard \u2014 it generates your config
|
|
22567
|
+
3. Copy the config to your project and restart your AI tool
|
|
22568
|
+
|
|
22569
|
+
If you already have an account, check that both **PAPI_PROJECT_ID** and **PAPI_DATA_API_KEY** are set in your .mcp.json env config.`
|
|
22570
|
+
}]
|
|
22571
|
+
}));
|
|
22572
|
+
return errorServer;
|
|
22573
|
+
};
|
|
22574
|
+
const httpServer = createHttpServer((req, res) => {
|
|
22575
|
+
if (req.method === "GET" && req.url === "/healthz") {
|
|
22576
|
+
res.writeHead(200, { "Content-Type": "text/plain" });
|
|
22577
|
+
res.end("ok");
|
|
22578
|
+
return;
|
|
22579
|
+
}
|
|
22580
|
+
if (httpToken) {
|
|
22581
|
+
const authHeader = req.headers["authorization"] ?? "";
|
|
22582
|
+
const provided = authHeader.startsWith("Bearer ") ? authHeader.slice(7) : "";
|
|
22583
|
+
if (provided !== httpToken) {
|
|
22584
|
+
res.writeHead(401, { "Content-Type": "application/json" });
|
|
22585
|
+
res.end(JSON.stringify({ error: "Unauthorized" }));
|
|
22586
|
+
return;
|
|
22587
|
+
}
|
|
22588
|
+
}
|
|
22589
|
+
if (req.url === "/mcp" || req.url === "/sse") {
|
|
22590
|
+
if (req.method === "POST") {
|
|
22591
|
+
const chunks = [];
|
|
22592
|
+
req.on("data", (chunk) => chunks.push(chunk));
|
|
22593
|
+
req.on("end", () => {
|
|
22594
|
+
let parsedBody;
|
|
22595
|
+
try {
|
|
22596
|
+
parsedBody = JSON.parse(Buffer.concat(chunks).toString("utf-8"));
|
|
22597
|
+
} catch {
|
|
22598
|
+
res.writeHead(400, { "Content-Type": "application/json" });
|
|
22599
|
+
res.end(JSON.stringify({ error: "Invalid JSON body" }));
|
|
22600
|
+
return;
|
|
22601
|
+
}
|
|
22602
|
+
(async () => {
|
|
22603
|
+
const transport = new StreamableHTTPServerTransport({ sessionIdGenerator: void 0 });
|
|
22604
|
+
const reqServer = createServerForRequest();
|
|
22605
|
+
await reqServer.connect(transport);
|
|
22606
|
+
await transport.handleRequest(req, res, parsedBody);
|
|
22607
|
+
await reqServer.close();
|
|
22608
|
+
})().catch((err) => {
|
|
22609
|
+
process.stderr.write(`[papi] HTTP transport error: ${err instanceof Error ? err.message : String(err)}
|
|
22610
|
+
`);
|
|
22611
|
+
if (!res.headersSent) {
|
|
22612
|
+
res.writeHead(500, { "Content-Type": "application/json" });
|
|
22613
|
+
res.end(JSON.stringify({ error: "Internal server error" }));
|
|
22614
|
+
}
|
|
22615
|
+
});
|
|
22616
|
+
});
|
|
22617
|
+
return;
|
|
22618
|
+
}
|
|
22619
|
+
(async () => {
|
|
22620
|
+
const transport = new StreamableHTTPServerTransport({ sessionIdGenerator: void 0 });
|
|
22621
|
+
const reqServer = createServerForRequest();
|
|
22622
|
+
await reqServer.connect(transport);
|
|
22623
|
+
await transport.handleRequest(req, res);
|
|
22624
|
+
await reqServer.close();
|
|
22625
|
+
})().catch((err) => {
|
|
22626
|
+
process.stderr.write(`[papi] HTTP transport error: ${err instanceof Error ? err.message : String(err)}
|
|
22627
|
+
`);
|
|
22628
|
+
if (!res.headersSent) {
|
|
22629
|
+
res.writeHead(500, { "Content-Type": "application/json" });
|
|
22630
|
+
res.end(JSON.stringify({ error: "Internal server error" }));
|
|
22631
|
+
}
|
|
22632
|
+
});
|
|
22633
|
+
return;
|
|
22634
|
+
}
|
|
22635
|
+
res.writeHead(404, { "Content-Type": "text/plain" });
|
|
22636
|
+
res.end("Not found");
|
|
22637
|
+
});
|
|
22638
|
+
httpServer.listen(httpPort, httpHost, () => {
|
|
22639
|
+
process.stderr.write(`[papi] HTTP transport listening on http://${httpHost}:${httpPort}/mcp
|
|
22640
|
+
`);
|
|
22641
|
+
});
|
|
22642
|
+
} else {
|
|
22643
|
+
const transport = new StdioServerTransport();
|
|
22644
|
+
await server.connect(transport);
|
|
22645
|
+
}
|
package/package.json
CHANGED