## Summary
Adding `https://api.twenty.com/mcp` as an MCP server in Claude fails
with `Couldn't reach the MCP server` before OAuth can start. Two
independent bugs cause this:
1. **Missing path-aware well-known route.** The latest MCP spec
instructs clients to probe `/.well-known/oauth-protected-resource/mcp`
before `/.well-known/oauth-protected-resource`. Only the root path was
registered, so the path-aware request fell through to
`ServeStaticModule` and returned the SPA's `index.html` with HTTP 200.
Strict clients (Claude.ai) tried to parse it as JSON and gave up. Fixed
by registering both paths on the same handler.
2. **Stale protocol version.** Server advertised `2024-11-05`, which
predates Streamable HTTP. We've implemented Streamable HTTP (SSE
response format was added in #19528), so bumped to `2025-06-18`.
Reproduction before the fix:
```
$ curl -s -o /dev/null -w "%{http_code} %{content_type}\n" https://api.twenty.com/.well-known/oauth-protected-resource/mcp
200 text/html; charset=UTF-8
```
After the fix this returns `application/json` with the RFC 9728 metadata
document.
Note: this is separate from #19755 (host-aware resource URL for
multi-host deployments).
## Test plan
- [x] `npx jest oauth-discovery.controller` — 2/2 tests pass, including
one asserting both routes are registered
- [x] `npx nx lint:diff-with-main twenty-server` passes
- [ ] After deploy, `curl
https://api.twenty.com/.well-known/oauth-protected-resource/mcp` returns
JSON (not HTML)
- [ ] Adding `https://api.twenty.com/mcp` in Claude reaches the OAuth
authorization screen
🤖 Generated with [Claude Code](https://claude.com/claude-code)
---------
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
## Summary
- Add SSE (`text/event-stream`) as an alternative response format on
`POST /mcp` per the MCP streamable-http spec
- When clients send `Accept: text/event-stream`, the server responds
with SSE wire format; otherwise returns JSON as before (fully backwards
compatible)
- Emit a `notifications/progress` SSE event before tool execution to
signal long-running operations
- No sessions, no GET SSE, no protocol version bump — this is
transport-level only (Phase 2)
## Changes
- **New**: `write-sse-event.util.ts` — writes correctly formatted SSE
events to Express Response
- **New**: `mcp-progress-notification.const.ts` — constants for progress
notification method and token prefix
- **Modified**: `mcp-core.controller.ts` — checks `Accept` header,
branches into SSE vs JSON response path
- **Modified**: `mcp-protocol.service.ts` — passes optional `sseWriter`
callback to tool executor
- **Modified**: `mcp-tool-executor.service.ts` — emits progress
notification via `sseWriter` before tool execution
- **Tests**: Unit tests for SSE utility, controller SSE/JSON paths, tool
executor progress notifications, and integration tests for SSE streaming
## Test plan
- [x] Unit tests: `writeSseEvent` utility produces correct SSE wire
format
- [x] Unit tests: Controller returns SSE headers and writes events when
`Accept: text/event-stream`
- [x] Unit tests: Controller returns JSON when `Accept:
application/json` only
- [x] Unit tests: Notifications (no `id`) return 202 regardless of
Accept header
- [x] Unit tests: Tool executor emits progress notification via
sseWriter
- [x] Unit tests: Tool executor works without sseWriter (backwards
compatible)
- [x] Integration tests: SSE response for ping, JSON fallback, progress
notification before tool call
- [x] All 503 test suites pass, typecheck clean
https://claude.ai/code/session_01QrqjBUXePJkPMd6gBAoWaR
---------
Co-authored-by: Claude <noreply@anthropic.com>
## Summary
Fix MCP `/mcp` transport handling for clients using `streamable-http`.
## Changes
- add explicit `GET /mcp` and `DELETE /mcp` handlers
- return `405 Method Not Allowed` with `Allow: POST`
- keep `POST /mcp` protected by MCP auth guards
- mark `GET` and `DELETE` as intentionally public with
`PublicEndpointGuard` + `NoPermissionGuard`
- update the advertised MCP protocol version to `2025-03-26`
- add unit and integration coverage for the new behavior
## Why
The frontend advertises the MCP server as `streamable-http`, but the
backend only effectively handled `POST /mcp`. Some MCP clients probe
`GET /mcp` during connection setup, so unsupported methods need explicit
method-level responses instead of falling through or being blocked
before the handler.
## Validation
- verified locally:
- `GET /mcp` -> `405`
- `DELETE /mcp` -> `405`
- unauthenticated `POST /mcp` -> `401`
- passed controller unit tests
- added integration assertions for `GET` and `DELETE`
---------
Co-authored-by: Claude <noreply@anthropic.com>
Co-authored-by: Félix Malfait <felix.malfait@gmail.com>
## Summary
Fixes#18524
Fixes the MCP response contract for non-`initialize` methods.
Previously, `/mcp` returned initialize-style metadata for methods like
`tools/list`, which caused strict MCP clients to reject the response
shape. The endpoint also returned `201 Created` for RPC calls even
though no resource was being created.
## Changes
- return only method-specific payloads for MCP list methods
- `tools/list` -> `{ tools: [...] }`
- `prompts/list` -> `{ prompts: [] }`
- `resources/list` -> `{ resources: [] }`
- keep MCP server metadata only on `initialize`
- make `/mcp` return `200 OK` instead of `201 Created`
- add regression tests for:
- `tools/list` response shape
- `prompts/list` response shape
- `resources/list` response shape
## Why
Strict MCP clients expect:
- standard RPC transport semantics over HTTP
- method-specific JSON-RPC result payloads
Returning initialize metadata for non-`initialize` methods breaks that
expectation and can cause client deserialization or protocol validation
failures.
## Verification
- reproduced the issue locally against `/mcp`
- verified `tools/list` was previously returning initialize-style fields
- verified `tools/list` now returns only `result.tools`
- verified `/mcp` now returns `200 OK`
- ran targeted Jest tests:
```bash
cd /Users/apple/MyProjects/OpenSource/twenty/packages/twenty-server
npx jest --runInBand src/engine/api/mcp/services/__tests__/mcp-protocol.service.spec.ts src/engine/api/mcp/services/__tests__/mcp-tool-executor.service.spec.ts
---------
Co-authored-by: Félix Malfait <felix@twenty.com>
…PC validation
- Introduced full integration test suite for MCP controller, testing
`POST /mcp` with valid and invalid payloads.
- Added `@IsDefined` validation to ensure the `method` field is required
in JSON-RPC requests.
- Applied `RestApiExceptionFilter` to MCP controller for consistent
error handling.
- Enhanced validation pipe in MCP controller to whitelist and reject
non-whitelisted properties.
- Consolidated exception filters in SSOAuthController.