Skip to content

feat(integrations): Twenty CRM + Chatwoot, native HTTP MCP transport, Linear create fix - #406

Merged
agjs merged 2 commits into
mainfrom
feat/twenty-chatwoot-http-mcp
Sep 29, 2026
Merged

agjs merged 2 commits into
mainfrom
feat/twenty-chatwoot-http-mcp

Conversation

@agjs

@agjs agjs commented Sep 29, 2026

Copy link
Copy Markdown
Collaborator

What

  • Linear fix: linear_write create sent both team and teamId. The hosted Linear MCP rejects unknown keys, so every create failed ("Unrecognized key: teamId"). Every Linear call now sends only schema keys: comment uses issueId (never id, which edits a comment), comments sends issueId only, and mine asks list_issues for assignee: "me" (before, it listed everyone's issues). team is documented as required and checked up front.
  • Native HTTP MCP transport (type: "http" + headers): Streamable HTTP, JSON or SSE replies (read only until our response, so an open stream can't stall), session id + protocol-version echo, re-initialize on a forgotten session, per-call timeout, DELETE on close. Until now http entries were skipped as "not yet supported".
  • Twenty CRM: twenty_read (search / list / record with attached notes and tasks / pipeline) and twenty_write (create / update / note / task; no delete). Twenty's MCP is a catalog → learn_tools → execute_tool meta-surface over ~300 tools. The verbs call execute_tool with the exact shapes Twenty validates (select on reads, position on creates, composite name/emails/domainName, amounts in micros).
  • Chatwoot (REST, no MCP exists): chatwoot_read (conversations, transcript, contacts, inboxes/agents/labels) and chatwoot_write (reply, private note, status, assign, label). A reply is customer-visible, so the guidance says send only when asked and otherwise leave a private-note draft. Labels merge (Chatwoot's endpoint replaces the set). The token never follows a redirect, and customer text is marked as untrusted data.
  • Config: chatwootUrl / chatwootToken / chatwootAccountId in ~/.tsforge/config.json; TSFORGE_NO_TWENTY, TSFORGE_TWENTY_RAW, TSFORGE_NO_CHATWOOT.
  • Docs: new Twenty and Chatwoot pages; MCP page covers the http transport; Linear team; settings/flags.

How it's tested

  • Strict fakes that reject unknown fields: the check that would have caught the Linear bug. The 5 new Linear tests fail on main with the exact production error.
  • Twenty calls are checked against Twenty's real v2.41 input schemas plus each object's real field names (tests/fixtures/twenty-schemas.json: metadata only, no CRM data). Wrong field, missing select/position, bad enum, or an unknown select name all fail as they would live.
  • HTTP transport tests run against a real local Bun.serve MCP server (JSON, SSE, SSE-never-closes, auth, sessions, 404 re-init, timeout, isError, close).
  • Mutation-checked: 40 deliberate breaks across the transport, Chatwoot and Twenty ops, and the tests catch every one (two survivors led to the field-name check).
  • Live contract suite (TSFORGE_LIVE=1, opt-in, skipped in CI) against real self-hosted instances. Twenty reads and a full create → update → note → task → read-back → destroy cycle pass. Chatwoot reads pass. The live runs caught two bugs the fakes couldn't: Twenty's group_by shape (dimensions/value), and soft-deleted records keeping unique domains (the duplicate error is now explained in plain words).
  • bun run ci:local: 6311 pass, 0 fail. Docs build is clean.

Not in this PR

notion-ops / sentry-ops guess payload keys the same way the Linear bug did (e.g. { id, pageId, page_id }). Worth the same strict-schema treatment in a follow-up.

… Linear create fix

- fix(linear): linear_write create sent both `team` and `teamId`; the hosted
  Linear MCP rejects unknown keys, so every create failed. Send only schema
  keys everywhere: comment uses issueId (never `id`, which edits a comment),
  comments read sends issueId only, mine asks list_issues for assignee "me".
  `team` is now documented as required and checked before the call.
- feat(mcp): Streamable HTTP transport (`type: "http"`) with auth `headers`,
  session id + protocol version echo, re-initialize on a forgotten session,
  JSON or SSE replies (read until our response, so an open stream can't
  stall), per-call timeout, DELETE on close. Previously http entries were
  skipped as "not yet supported".
- feat(twenty): twenty_read (search/list/record/pipeline) and twenty_write
  (create/update/note/task, no delete) over Twenty's execute_tool, sending
  the exact shapes it validates (select on reads, position on creates,
  composite name/emails/domainName, amounts in micros).
- feat(chatwoot): chatwoot_read / chatwoot_write over Chatwoot's REST API
  (it has no MCP): conversations, transcript, contacts, inboxes/agents/labels;
  reply (customer-visible, guidance says only when asked), private note,
  status, assign, label (merged, add-only). Token never follows a redirect;
  customer text is marked as untrusted data.
- config: chatwootUrl/chatwootToken/chatwootAccountId in ~/.tsforge/config.json;
  TSFORGE_NO_TWENTY, TSFORGE_TWENTY_RAW, TSFORGE_NO_CHATWOOT.
- tests: strict fakes that reject unknown fields (the check that would have
  caught the Linear bug), Twenty calls validated against its real v2.41 input
  schemas + field names (fixture), a real local HTTP server for the transport,
  mutation-checked; opt-in live suite (TSFORGE_LIVE=1) against real instances.
- docs: Twenty and Chatwoot pages; MCP http transport; Linear team; settings.
@cloudflare-workers-and-pages

cloudflare-workers-and-pages Bot commented Sep 29, 2026 •

Copy link
Copy Markdown

Deploying with  Cloudflare Workers  Cloudflare Workers

The latest updates on your project. Learn more about integrating Git with Workers.

Status Name Latest Commit Preview URL Updated (UTC)
✅ Deployment successful!
View logs
tsforge 21d1797 Commit Preview URL

Branch Preview URL
Sep 29 2026, 11:58 AM

GHSA-3wwx-pv8p-q78v: WebSocket permessage-deflate DoS in undici >=7.28.0
<7.29.1. Reaches the tree through the root override and miniflare's exact
7.29.0 pin; the override lifts both to 7.30.0. osv-scanner: no issues.
@agjs
agjs merged commit 54798a5 into main Sep 29, 2026
9 checks passed
@agjs
agjs deleted the feat/twenty-chatwoot-http-mcp branch September 29, 2026 12:11
@agjs agjs mentioned this pull request Sep 29, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant