Skip to content

test(mcp): add test suite for mcp.ts handler and fix session header bug - #189

Merged
niallroche merged 1 commit into
accordproject:mainfrom
Satvik77777:test/188-mcp-handler-test-suite
Jul 22, 2026
Merged

test(mcp): add test suite for mcp.ts handler and fix session header bug#189
niallroche merged 1 commit into
accordproject:mainfrom
Satvik77777:test/188-mcp-handler-test-suite

Conversation

@Satvik77777

@Satvik77777 Satvik77777 commented Jun 5, 2026

Copy link
Copy Markdown
Contributor

Summary

Adds the first test suite for mcp.ts which previously had 0% test
coverage. The session-header bug fix and session cleanup discovered
during testing have been split into separate PRs (#203 and #194) per
@niallroche's suggestion.

What this PR does

Refactor — export getServer() for testability

  • Exports getServer() as a named export so tools and resources can be
    tested without fighting the StreamableHTTPServerTransport stream
    mechanics

Tests (7 new, 60 total passing)

  • POST /mcp without session ID returns 400
  • POST /messages with bad session ID returns 400
  • getTemplate tool is registered correctly
  • getAgreement tool is registered correctly
  • trigger-agreement tool is registered correctly
  • Tools tested via InMemoryTransport from official MCP SDK — bypasses
    HTTP stream mechanics entirely
  • Full baseline (53 tests) still passing

Why InMemoryTransport over supertest

StreamableHTTPServerTransport requires specific Accept headers and
stream mechanics that supertest cannot emulate. Using InMemoryTransport
from the official @modelcontextprotocol/sdk tests the actual tool logic
with 100% confidence.

References

Author Checklist

  • DCO sign-off provided
  • Tests added and passing (60/60)
  • No behavior changes

@Satvik77777

Satvik77777 commented Jun 5, 2026

Copy link
Copy Markdown
Contributor Author

Hi @niallroche @mttrbrts — this PR adds the first test
suite for mcp.ts (previously 0% coverage). Found and
fixed a real bug during testing: mcp-session-id header
was never returned to clients on initialization.
Tests use InMemoryTransport from the official MCP SDK
for reliable tool testing. 60/60 passing, DCO signed.
This directly supports Jay's GSoC work on RFC #143.

@JayDS22

JayDS22 commented Jun 5, 2026

Copy link
Copy Markdown
Collaborator

Nice work on this @Satvik77777. A few thoughts:

InMemoryTransport approach is the right call. Testing the MCP handler via supertest would have been a slog given the StreamableHTTP stream mechanics. Using @modelcontextprotocol/sdk's InMemoryTransport sidesteps the transport layer cleanly and tests the actual tool logic with confidence. Worth mentioning in the PR description as a pattern others can follow.

Session-id header fix is a real bug. Validating mcp-session-id on incoming requests without sending it back on initialization breaks any client that follows the spec. Good catch. One small suggestion: a test exercising the new res.setHeader('mcp-session-id', transport.sessionId) line on init would lock the fix in against future regression. Otherwise it's a bug-fix-without-a-test which can come back later.

Coordination note on follow-up tests: I have a planned follow-up PR that adds contract tests for the buildApiErrorMessage helper across HTTP failure modes (once #155 lands and #153 is closed). The test scope is different from yours: you're covering tool registration plumbing, I'd cover helper behavior. My PR will append to the mcp.test.ts file you're adding here rather than create it. Flagging now in case you want to coordinate on the structure (e.g. top-level describe blocks per area).

No line conflict with my open #155 or #184. LGTM from my side modulo the test-the-fix nit above.

@Satvik77777

Copy link
Copy Markdown
Contributor Author

Thanks @JayDS22 — really like the thorough review.

Pushed a regression test that locks in the session-id
header fix. It verifies that res.setHeader('mcp-session-id',
transport.sessionId) fires correctly on initialization
so the fix can't silently regress.

Good to know about your planned buildApiErrorMessage
contract tests after #155 lands. Happy to coordinate
on the describe block structure — I can organize
mcp.test.ts with clear top-level sections:

  • describe('MCP HTTP Router') — what I have now
  • describe('MCP Tool Logic') — tool registration tests
  • describe('MCP Helpers') — for your buildApiErrorMessage tests

That way your follow-up PR has a clean place to append
without restructuring.

@github-actions

Copy link
Copy Markdown

This PR is stale because it has been open with no activity. Remove the stale label or comment to keep it active. Only items with maintainer engagement are auto-closed.

@github-actions github-actions Bot added the Stale label Jun 24, 2026
@niallroche

Copy link
Copy Markdown
Contributor

Thanks for this @Satvik77777 in the interests of cleaning up multiple issues and getting a clean merge with other PRs that are in the process of being merged can I suggest as this PR bundles three distinct things: the test suite, the session cleanup (also in #194), and a standalone bug fix — the mcp-session-id response header never being set on new session initialization. That header fix is genuinely independent and valuable on its own.

My suggested split:

Extract the res.setHeader('mcp-session-id', newSessionId) change plus a focused test for it as a separate PR. That can merge immediately with no conflicts.
Let #194 carry the session cleanup (sessionLastActivity, setInterval, .unref()) — it's already scoped correctly.
The broader test suite can then be rebased onto both and land after, with mcp.test.ts appending to whatever #196 establishes.

@JayDS22

JayDS22 commented Jun 25, 2026

Copy link
Copy Markdown
Collaborator

Hi @Satvik77777, flagging Niall's suggestion in the merge plan thread: splitting the session-header fix (the 6-line change in mcp.ts) out as its own PR so it can land independently of the test suite. Then once #194 (session cleanup) lands, the test-suite portion of this PR rebases cleanly on top.

If you're busy with #187 / #192 / #194 I'm happy to do the split off your branch (preserving authorship via Co-authored-by) and open the session-header-only PR. Or if you'd rather drive it yourself, just say the word.

One other thing: parse.js and parse-endpoint.js at the repo root in this diff look like local debug scaffolding rather than intended deliverables. Happy to include dropping them in the same split if you'd like, or leave them for you to address.

Either way works for me, just want to keep the merge train moving per Niall's plan.

@Satvik77777

Copy link
Copy Markdown
Contributor Author

Thanks @niallroche @JayDS22 — driving the split

Will rebase this test suite onto both once #194 and #203 land

@Satvik77777
Satvik77777 force-pushed the test/188-mcp-handler-test-suite branch from 65930b9 to a216259 Compare June 25, 2026 21:53
Copilot AI review requested due to automatic review settings June 25, 2026 21:53

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds initial automated coverage for the MCP handler and fixes MCP Streamable HTTP initialization to return an mcp-session-id header so clients can persist the session.

Changes:

  • Export getServer() from server/handlers/mcp.ts to enable direct, transport-independent testing of MCP tool/resource registration.
  • Fix Streamable HTTP initialization to send mcp-session-id back to clients.
  • Add a new Jest test suite for MCP HTTP boundary behavior and in-memory tool execution.

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 6 comments.

File Description
server/handlers/mcp.ts Exports getServer() and returns mcp-session-id on initialization responses.
server/handlers/mcp.test.ts Introduces tests for MCP HTTP boundary validation and tool/resource registration via InMemoryTransport.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +1 to +5
import request from 'supertest';
import express from 'express';
import { jest } from '@jest/globals';
import * as crypto from 'crypto';

Comment on lines +44 to +47
app = express();
app.use(express.json());
app.use('/mcp', mcpRouter);
});
Comment on lines +55 to +58
const response = await request(app)
.post('/mcp/mcp')
.send({ jsonrpc: '2.0', method: 'tools/list', id: 1 })
.expect(400);
Comment thread server/handlers/mcp.test.ts Outdated
Comment on lines +70 to +86
it('returns mcp-session-id header on initialization response', async () => {
const res = await request(app)
.post('/mcp/mcp')
.send({
jsonrpc: '2.0',
id: 1,
method: 'initialize',
params: {
protocolVersion: '2024-11-05',
capabilities: {},
clientInfo: { name: 'test', version: '1.0' }
}
});
// The mcp-session-id header must be present on init response
// This locks in the bug fix against future regression
expect(res.headers['mcp-session-id']).toBeDefined();
});
Comment thread server/handlers/mcp.test.ts Outdated
Comment on lines +88 to +91
it('GET /mcp/sse returns an SSE stream with the correct content type', async () => {
const response = await request(app)
.get('/mcp/sse')
.buffer(false)
Comment thread server/handlers/mcp.test.ts Outdated
Comment on lines +109 to +114
it('POST /mcp/messages returns 400 when sessionId query param is missing or invalid', async () => {
const response = await request(app)
.post('/mcp/messages')
.query({ sessionId: 'nonexistent' })
.send({ jsonrpc: '2.0', method: 'ping', id: 1 })
.expect(400);
@github-actions github-actions Bot removed the Stale label Jun 26, 2026
@JayDS22

JayDS22 commented Jul 9, 2026

Copy link
Copy Markdown
Collaborator

@Satvik77777 apologies for the wait on this one. My mcp.ts queue has kept it stuck in a CONFLICTING state, since #200 landed and reshaped large sections of the file. Clearing #199#201 is priority this week, and I'll happily help with the rebase against the updated mcp.ts shape once they land. The conflicts should localize to the imports and describe blocks, similar to what I hit when rebasing #199 onto post-#200 main.

Your #194 and #203 look clean on a quick pass and should be able to move independently on their own review cycles. Left a peer note on #203 with a couple of small suggestions.

@Satvik77777

Copy link
Copy Markdown
Contributor Author

Thanks for the update @JayDS22 — no rush on #189, appreciate you flagging the conflict source (#200 reshaping mcp.ts). Happy to help with the rebase once #199/#201 land if useful. Good to know #194/#203 can move independently in the meantime.

@JayDS22

JayDS22 commented Jul 9, 2026

Copy link
Copy Markdown
Collaborator

@Satvik77777 quick heads-up: #199 and #202 both landed today, so main has moved past what your branch was rebased against. #189 should be able to rebase cleanly against post-#199 main now (the conflicts should localize to the imports block and the mcp.test.ts describe blocks, similar to what I hit when I rebased #199 and just did on #201). Ping me if you want to pair on it, otherwise I will keep an eye on it and offer feedback once you have a fresh push.

@Satvik77777
Satvik77777 force-pushed the test/188-mcp-handler-test-suite branch from 8caefcb to ea98591 Compare July 10, 2026 15:26
@Satvik77777

Copy link
Copy Markdown
Contributor Author

@JayDS22 — rebased #189 against latest main (post #199/#202). Conflicts were localized to mcp.test.ts as you predicted — combined both test suites (upstream's error-handling/typed-error/Concerto tests + this PR's HTTP boundary/InMemoryTransport tests). Also dropped the old 9342f8c commit entirely (both its test and its mcp.ts hunk), since that session-header fix now lives solely in #203 to avoid duplicate logic. All 23 tests passing. Ready whenever you get a chance to take a look.

@niallroche
niallroche self-requested a review July 17, 2026 14:44
@niallroche

Copy link
Copy Markdown
Contributor

can you resolve the test and I will merge @Satvik77777

@Satvik77777
Satvik77777 force-pushed the test/188-mcp-handler-test-suite branch from ea98591 to 8f04f85 Compare July 18, 2026 09:53
@niallroche

Copy link
Copy Markdown
Contributor

Hey @Satvik77777 — quick steer on why CI is red, since it's not what it looks like.

The failure isn't the Copilot mount-path comment — you already fixed that. The test now mounts at / and hits /mcp / /sse / /messages directly, which is the correct production surface (index.ts mounts the router at /, and the /mcp prefix comes from the route declaration in mcp.ts). That part's good.

The actual failure is a syntax error in mcp.test.ts — tsc reports handlers/mcp.test.ts(497,1): error TS1005: '}' expected. Line 497 is EOF, so there are unclosed blocks: the brace/paren balance is short by two }), i.e. two describe blocks are missing their closers. tsc fails to compile, so CI dies in ~40s before any test runs. Looks like it slipped in when the mcp.test.ts conflict got resolved during the rebase — you and #203 both rewrite that file heavily, so it's an easy one to drop. Adding the two missing }) before EOF should turn it green.

Two things while you're in there:

This branch still conflicts with #203 in mcp.test.ts, and #189 depends on #203 landing first anyway — so once the syntax fix is in, please also rebase onto current main after #203 merges, not before, to avoid a second round.
Running npx tsc --noEmit locally before pushing will catch this class of thing instantly — worth it given how much these branches move around each other right now.
Production mcp.ts is untouched and fine; this is purely the test file. Thanks!

@Satvik77777
Satvik77777 force-pushed the test/188-mcp-handler-test-suite branch from 8f04f85 to 92ff5cd Compare July 18, 2026 10:51
@Satvik77777

Copy link
Copy Markdown
Contributor Author

@niallroche — thanks for catching that, matches exactly what I found independently (two missing closing braces in mcp.test.ts from the rebase conflict resolution). Fixed and pushed (92ff5cd) — all 14 checks passing now. Holding off on rebasing onto current main again until #203 merges, per your note, to avoid a second conflict round.

@niallroche

Copy link
Copy Markdown
Contributor

this should be ready if you rebase @Satvik77777

Resolves test coverage gap for the new MCP handler.
- Extracted getServer() for testability.
- Wrote tests for HTTP boundary error cases (400 responses).
- Wrote internal tests using InMemoryTransport to verify getTemplate, getAgreement, and trigger-agreement tools directly without battling strict StreamableHTTPServerTransport payload requirements.

Signed-off-by: Satvik77777 <satviksaini02@gmail.com>
@Satvik77777
Satvik77777 force-pushed the test/188-mcp-handler-test-suite branch from 92ff5cd to a8e2cd9 Compare July 22, 2026 09:04
@Satvik77777

Copy link
Copy Markdown
Contributor Author

@niallroche — rebased onto current main (post-#216/#214 service-layer slices), force-pushed (same single commit rebased, 92ff5cda8e2cd9). The getServer conflict needed both the export keyword (for testability) and the new db: Database parameter (from the service-layer work) — merged both in. Test file combined cleanly: kept the session-header test plus this PR's HTTP boundary/internal logic suites, using a createMockDb() factory (same pattern already established in templateService.test.ts) wired via res.locals.db middleware. Clean tsc --noEmit, 148/148 tests passing. Should be ready for merge.

@niallroche
niallroche merged commit fddc35d into accordproject:main Jul 22, 2026
15 checks passed
@github-actions

Copy link
Copy Markdown

🎉 Thank you for your contribution! 🎉

Dear @Satvik77777,

Your pull request has been successfully merged into the project! We greatly appreciate your efforts and the time you've dedicated to improving our repository.

What happens next?

  • Your changes will be included in the next release
  • Your name will be added to our contributors list
  • Feel free to take on another issue or suggest new features

Once again, thank you for being part of our community!

Best regards,
The APAP Team

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

maintainer-engaged A maintainer has commented or reviewed this item

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Title: test(mcp): add test suite for mcp.ts handler (0% coverage)

4 participants