@poncho-ai/harness 0.40.1 → 0.41.0

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/test/mcp.test.ts CHANGED
@@ -442,4 +442,178 @@ describe("mcp bridge protocol transports", () => {
442
442
  await bridge.stopLocalServers();
443
443
  await new Promise<void>((resolveClose) => server.close(() => resolveClose()));
444
444
  });
445
+
446
+ it("re-initializes and retries when the server expires the session (404)", async () => {
447
+ process.env.LINEAR_TOKEN = "token-123";
448
+ let sessionCounter = 0;
449
+ let activeSession = "";
450
+ let initializeCount = 0;
451
+ let toolCallAttempts = 0;
452
+ const server = createServer(async (req, res) => {
453
+ if (req.method === "DELETE") {
454
+ res.statusCode = 200;
455
+ res.end();
456
+ return;
457
+ }
458
+ const chunks: Buffer[] = [];
459
+ for await (const chunk of req) chunks.push(Buffer.from(chunk));
460
+ const body = Buffer.concat(chunks).toString("utf8");
461
+ const payload = body.trim().length > 0 ? (JSON.parse(body) as any) : {};
462
+ if (payload.method === "initialize") {
463
+ initializeCount += 1;
464
+ sessionCounter += 1;
465
+ activeSession = `session_${sessionCounter}`;
466
+ res.setHeader("Content-Type", "application/json");
467
+ res.setHeader("Mcp-Session-Id", activeSession);
468
+ res.end(
469
+ JSON.stringify({
470
+ jsonrpc: "2.0",
471
+ id: payload.id,
472
+ result: {
473
+ protocolVersion: "2025-03-26",
474
+ capabilities: { tools: { listChanged: true } },
475
+ serverInfo: { name: "remote", version: "1.0.0" },
476
+ },
477
+ }),
478
+ );
479
+ return;
480
+ }
481
+ if (payload.method === "notifications/initialized") {
482
+ res.statusCode = 202;
483
+ res.end();
484
+ return;
485
+ }
486
+ if (req.headers["mcp-session-id"] !== activeSession) {
487
+ res.statusCode = 404;
488
+ res.end();
489
+ return;
490
+ }
491
+ if (payload.method === "tools/list") {
492
+ res.setHeader("Content-Type", "application/json");
493
+ res.end(
494
+ JSON.stringify({
495
+ jsonrpc: "2.0",
496
+ id: payload.id,
497
+ result: {
498
+ tools: [
499
+ {
500
+ name: "ping",
501
+ inputSchema: {
502
+ type: "object",
503
+ properties: { value: { type: "string" } },
504
+ },
505
+ },
506
+ ],
507
+ },
508
+ }),
509
+ );
510
+ return;
511
+ }
512
+ if (payload.method === "tools/call") {
513
+ toolCallAttempts += 1;
514
+ // Simulate session expiry on the first tool call after discovery:
515
+ // invalidate the current session so the existing Mcp-Session-Id is stale,
516
+ // then return 404 once. The client should re-initialize and retry.
517
+ if (toolCallAttempts === 1) {
518
+ activeSession = "";
519
+ res.statusCode = 404;
520
+ res.end();
521
+ return;
522
+ }
523
+ res.setHeader("Content-Type", "application/json");
524
+ res.end(
525
+ JSON.stringify({
526
+ jsonrpc: "2.0",
527
+ id: payload.id,
528
+ result: { result: { echoed: payload.params?.arguments?.value ?? "" } },
529
+ }),
530
+ );
531
+ return;
532
+ }
533
+ res.statusCode = 404;
534
+ res.end();
535
+ });
536
+ await new Promise<void>((r) => server.listen(0, () => r()));
537
+ const address = server.address();
538
+ if (!address || typeof address === "string") throw new Error("Unexpected address");
539
+
540
+ const bridge = new LocalMcpBridge({
541
+ mcp: [
542
+ {
543
+ name: "remote",
544
+ url: `http://127.0.0.1:${address.port}/mcp`,
545
+ auth: { type: "bearer", tokenEnv: "LINEAR_TOKEN" },
546
+ },
547
+ ],
548
+ });
549
+
550
+ await bridge.startLocalServers();
551
+ await bridge.discoverTools();
552
+ const tools = await bridge.loadTools(["remote/ping"]);
553
+ const tool = tools.find((entry) => entry.name === "remote/ping");
554
+ expect(tool).toBeDefined();
555
+
556
+ const output = await tool?.handler(
557
+ { value: "after-expiry" },
558
+ {
559
+ agentId: "agent",
560
+ runId: "run",
561
+ step: 1,
562
+ workingDir: process.cwd(),
563
+ parameters: {},
564
+ },
565
+ );
566
+
567
+ expect(output).toEqual({ echoed: "after-expiry" });
568
+ expect(toolCallAttempts).toBe(2);
569
+ expect(initializeCount).toBe(2);
570
+
571
+ await bridge.stopLocalServers();
572
+ await new Promise<void>((r) => server.close(() => r()));
573
+ });
574
+
575
+ it("does not retry on 404 when no session has been established", async () => {
576
+ process.env.LINEAR_TOKEN = "token-123";
577
+ let initializeCount = 0;
578
+ const server = createServer(async (req, res) => {
579
+ if (req.method === "DELETE") {
580
+ res.statusCode = 200;
581
+ res.end();
582
+ return;
583
+ }
584
+ const chunks: Buffer[] = [];
585
+ for await (const chunk of req) chunks.push(Buffer.from(chunk));
586
+ const body = Buffer.concat(chunks).toString("utf8");
587
+ const payload = body.trim().length > 0 ? (JSON.parse(body) as any) : {};
588
+ if (payload.method === "initialize") {
589
+ initializeCount += 1;
590
+ // No Mcp-Session-Id: server-side 404 here is a true endpoint failure, not session expiry.
591
+ res.statusCode = 404;
592
+ res.end();
593
+ return;
594
+ }
595
+ res.statusCode = 404;
596
+ res.end();
597
+ });
598
+ await new Promise<void>((r) => server.listen(0, () => r()));
599
+ const address = server.address();
600
+ if (!address || typeof address === "string") throw new Error("Unexpected address");
601
+
602
+ const bridge = new LocalMcpBridge({
603
+ mcp: [
604
+ {
605
+ name: "remote",
606
+ url: `http://127.0.0.1:${address.port}/mcp`,
607
+ auth: { type: "bearer", tokenEnv: "LINEAR_TOKEN" },
608
+ },
609
+ ],
610
+ });
611
+
612
+ await bridge.startLocalServers();
613
+ await bridge.discoverTools();
614
+ expect(initializeCount).toBe(1);
615
+
616
+ await bridge.stopLocalServers();
617
+ await new Promise<void>((r) => server.close(() => r()));
618
+ });
445
619
  });