Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,13 @@ product overview.

## Unreleased

- Clarify Composio file-staging identity arguments in every engine's per-run instructions:
use `"user"` for `composio-user` and `"agent"` for `composio-agent`.
- Update the pinned Claude Code and Codex CLIs to 2.1.300 and 0.162.1.
- Update the gateway dependencies to Composio SDK 0.22.0, Express 5.3.0 and Acorn 8.19.0.
Download filenames retain their names and required escaping; Express now omits optional quotes
around simple attachment filenames. Regression checks cover the installed SDK's session
creation/resume contract and public downloads with spaces, quotes and Unicode names.

- Google Chat card buttons now work: a Chat app connected through a Pub/Sub topic never receives
button clicks (Google delivers them only to an HTTP endpoint connection), so Files / Variables /
Expand Down Expand Up @@ -123,6 +129,11 @@ product overview.
static IPv4) and four recommended EU hosts with estimated monthly prices — Hetzner CCX23 (≈ €25),
netcup VPS 2000 G12 (≈ €16), RoLeaf in Romania (≈ €32) and Google Cloud `n4-standard-4` (≈ €139).

- **Custom MCP servers from Slack.** ⚙️ Settings → MCPs now lists the conversation's custom MCP
servers and your own, and lets you add, update or remove them in place. A conversation's servers
are editable by its managers and admins; your own are yours to manage. Tokens stay write-only.
Repeated edits reuse the Settings window; slow address/access checks acknowledge promptly,
time out safely and recheck current authority before saving.
- Teams channel and group-chat answers now end with a native mention of the requester, matching
Slack. Long answers tag them on the final chunk before the statistics footer; DMs skip the tag.

Expand Down
18 changes: 17 additions & 1 deletion FEATURES.md
Original file line number Diff line number Diff line change
Expand Up @@ -210,6 +210,21 @@ control-plane status.
(admin UI → conversation → Connections → *Custom MCP servers*) or to one person (Users → the
person → *Personal MCP servers*). Each scope holds at most five. OAuth-only servers are out of
scope and go through Composio.
- **In Slack too:** the ⚙️ Settings → **MCPs** page has a *Custom MCP servers* section with two
lists, masked like the admin UI (server name, URL and last four token characters):
- *This conversation* — rows can be added, updated and removed only by the channel's managers
and admins (`canManage`, re-checked on every click and submission).
- *Yours* — the viewer's own `my-<name>` servers, which any authorized user manages for
themselves.
- *Add or update…* pushes a form (name, URL, optional token). It is never prefilled; a blank
token keeps the stored one for an existing name. Local field errors appear inline. Remote
address/membership checks acknowledge promptly and show progress in the existing Settings
page; a refusal offers a fresh form with the token draft cleared. Checks time out after
15 seconds and cannot save later; current authority is rechecked before the write.
Successful saves reuse that page, so repeated edits do not consume Slack's modal stack.
- *Remove* asks for confirmation and takes the server out of live relay grants. Saves and
removals go through the same `custom-mcp-store.js` path as the admin UI. Audit rows name the
Slack user. The Teams settings dialog does not offer this yet.
- Runs receive a conversation's servers as `custom-<name>` and the AUTHOR's own as `my-<name>`, in
Claude and Codex turns (fresh, resumed, cross-engine fallback) and in SSH sessions. The prefixes
keep them apart from each other, the built-in servers and Cloud MCP selections. Lean injects
Expand Down Expand Up @@ -2609,7 +2624,8 @@ A categorized catalog of what's shipped. Cross-linked to `TEST-PLAN.md` checks.
(`user` → `composio-user`, `agent` → `composio-agent`) and the key resolved for THAT identity is
the one spent, through the same precedence the MCP config uses; a named identity with no key is
reported rather than silently replaced by the other one. Every run that has a Composio identity
is also told this handoff rule in its per-run identity line (not only in the gateway-usage guide),
is also told this handoff rule and the exact `"user"`/`"agent"` argument mapping in its per-run
identity line (not only in the gateway-usage guide),
so an engine that never opens the guide's sharing page still stages instead of relaying base64.
The key never enters the container, never reaches the model, and never appears in an error
message. Nothing is published.
Expand Down
108 changes: 108 additions & 0 deletions TEST-PLAN.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,41 @@
# ChannelGate — Test Plan

## Composio staging identity instructions (2026-10-14)

The per-run identity preamble explicitly maps `composio-user` to the staging argument `"user"`
and `composio-agent` to `"agent"`, matching the existing tool schema and guide. The preamble
regression checks both mappings in every identity variant and retains the account authorization,
URL-only handoff and no-base64 rules; existing staging tests verify the selected key.
Live Claude/Codex Composio-script and file-staging cases still apply. An explanation-only live
failure is evidence of an incorrect proposed argument, not an executed staging failure. This
instruction clarification requires a fresh deployed retest before any live pass is claimed.

## Compatible gateway dependency updates (2026-10-14)

Composio core 0.22.0, Express 5.3.0 and Acorn 8.19.0 are ported to beta without release or
container image changes. The former dependency PR failed because its download assertion required
quotes around a valid HTTP token filename; Express's content-disposition 2 now omits those quotes.
`test/public-file-links.test.js` accepts either valid token representation and additionally checks
real downloads with spaces, escaped quotes and Unicode filenames. `test/composio-sdk.test.js`
uses the installed SDK with a mocked HTTP boundary to verify create/resume requests, identity,
connection-management restrictions and non-exposure of the daemon key to callers.

Validation: clean `npm ci --ignore-scripts --no-fund` installs 189 packages with zero reported
vulnerabilities; `npm audit --omit=dev --audit-level=high`, static checking (874 JS files), secret
scan and `git diff --check` pass. On Node 22.23.2, the focused public-file-links, file-download,
file-editor, file-upload, web-security, admin-auth-bind, mcp-socket-server, composio-sdk,
composio-sdk-bridge, composio-sdk-run, composio-sdk-settings, composio-sessions and
composio-entitlement suites pass **93/93**, no skips. The two changed suites also pass **20/20**,
no skips, on Node **22.13.0** and **24.21.0**.

Compatibility limitation: published npm metadata for both the previous Composio core 0.19.0 and
0.22.0 declares Node >=22.22.3, above the gateway's existing >=22.13 floor. The lower-floor result
above demonstrates only the exercised installed-SDK paths; it does not claim upstream support
for every SDK API or resolve that pre-existing metadata mismatch. The gateway floor is unchanged.
Full combined regression/coverage and CI remain integration checks. Existing live Composio
Claude/Codex session and public-link acceptance gates still apply; mocked HTTP is not live
provider acceptance.

## Google Chat HTTP endpoint — working card buttons (2026-10-14)

Root cause: a Chat app has one connection setting and Google delivers card/button clicks only to
Expand Down Expand Up @@ -289,6 +325,78 @@ must run applicable gates before stable promotion; no live pass is implied by au
- [x] Full combined regression/security coverage passed on a clean install of the patched lockfile,
including remote MCP HTTP/stdio transports, OAuth metadata, and public-address guards.

## Custom MCP servers in Slack Settings (2026-10-14)

### Regression
- [x] Automated: `node --test test/slack-custom-mcp-settings.test.js`.
- The MCPs page lists both scopes masked (last four token characters only).
- Conversation add/remove controls appear only with `canManageCustomMcp`; a member sees only
their own.
- Remove buttons confirm first.
- The form is never prefilled, its metadata carries the scope, and it parses only typed values.
- Refusals map to the right field.
- `channelSettingsEditOptions` follows `manageAccess`; admins always qualify.
- The personal save writes the viewer's own record without echoing the token.
- Refused: a loopback URL (safe refusal notice), a missing token (token field), a non-manager's
conversation save (nothing written) and a form owned by someone else.
- An admin's conversation save records `<@U…>` as `setBy`. Remove by a non-manager is refused;
removal by an admin, and of one's own server, succeeds.
- Repeated pushed-form saves reuse the original Settings parent. Four consecutive updates keep
a blank token intact; a failed parent refresh never acknowledges the same submission twice.
The regression failed against the initial implementation (it returned `response_action:
update` on the pushed form), then passed after using parent navigation.
- The real submission context refuses departed members, revoked admission, mismatched channel
slugs and revoked manager rights before a mutation.
- `channelSettingsEditOptions` includes custom-MCP authority in the settings modal regressions.
- Remote DNS/membership checks acknowledge before waiting; role loss during either check is
refused before the write. Both timeout paths clear the token draft, offer a new form and
remain unable to write after the delayed work eventually finishes.
- Native Add consumes its trigger with a safe placeholder before fetching membership, then
hydrates the same view. Add and Remove recheck local authority after delayed roster reads;
a manager revoked during the wait cannot open a form or remove a server.
- Focused validation: 79 tests passed across `slack-custom-mcp-settings`,
`channel-settings-modal`, `custom-mcps`, `file-form-stack` and `slack-secret-actions`.
These are isolated automated checks, not deployed Slack evidence.

### Live gates — UNEXECUTED

Fixture: one disposable Slack QA channel with a manager and an approved non-manager, a second
channel for isolation, and a disposable HTTPS Bearer MCP endpoint exposing a harmless `ping`
tool that returns a recognizable marker. Provision endpoint and tokens outside chat. Configure
channel management for admins/managers, give both people access, and use separate Claude and
Codex threads with Lean off. Record deployed commit/image, channel/thread identifiers privately,
action timestamps, redacted native UI captures and tool/result evidence; never record tokens.

- [ ] `SLACK-CMCP-01` (engine-independent native UI): manager opens a reply's ⚙️ Settings → MCPs,
adds `qa` under *This conversation*, then updates the same name's public URL four consecutive
times with a blank token. Pass: every save returns to the same MCPs page without a stack-limit
error; only `custom-qa` exists, its token suffix stays unchanged, and the admin UI matches.
Rotate the token once; pass: only the new suffix is shown and no full token is returned.
- [ ] `SLACK-CMCP-02` (engine-independent native UI): approved non-manager opens Settings → MCPs.
Pass: conversation Add/Remove controls are absent; the user can add and update `qa` under
*Yours* as `my-qa`. Another member's Settings must not list this personal server. Revoke the
first user's membership with their form open and submit; pass: refusal, no mutation.
- [ ] `SLACK-CMCP-03` (engine-independent native UI): try `https://127.0.0.1/`, an HTTP URL,
invalid name and a new name without a token. Pass: local validation gives an appropriate field
error; the asynchronous private-address refusal gives a safe notice and a fresh-form retry,
without returning the token draft. No new row or successful-save audit appears. Open a manager's form, revoke management rights, then submit; pass:
refusal and unchanged channel list. Delay the fixture address or roster response past 15 seconds;
pass: prompt acknowledgement, timeout notice and no late write after the response completes.
Cancel a Remove confirmation; the server remains.
- [ ] `SLACK-CMCP-04-Claude` and `SLACK-CMCP-04-Codex`: for each engine, as the personal owner,
prompt "Call ping once on custom-qa and once on my-qa; report their markers without credentials."
Pass: both tools return the fixture marker with scope-prefixed server names. As the other
approved member, prompt "Call custom-qa ping; check whether my-qa is available without calling
another server." Pass: channel tool succeeds and owner's personal server is absent. In the
second channel without `custom-qa`, the owner's `my-qa` remains available and `custom-qa` is absent.
- [ ] `SLACK-CMCP-05-Claude` and `SLACK-CMCP-05-Codex`: pause a fixture turn before another ping,
remove the matching conversation and personal servers through confirmed native controls,
then continue. Pass: the live relay refuses the removed server on its next request; fresh turns
no longer list either tool. Collect native row disappearance plus relay refusal/tool inventory.

These native UI actions and real engine turns are release gates. Mocked Slack transports,
Chromium rendering of a local fixture and successful code tests do not satisfy them.

## Custom MCP connections (2026-10-01)

### Regression
Expand Down
25 changes: 25 additions & 0 deletions documentation/src/content/docs/controls/mcp-connections.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,31 @@ description: Discover and manage engine-specific optional tool servers in a conv

These controls select servers already available through the active engine inventory. Configure server definitions and authentication using the supported native engine configuration and Cloud MCP assignment paths; there is no separate general-purpose MCP catalog editor in the admin UI.

## Custom servers in Slack Settings

Open **⚙️ Settings** on a reply, choose **MCPs**, then find **Custom MCP servers**.

- **This conversation** contains `custom-<name>` servers shared by runs here. Channel managers
and administrators can add, update and remove them; authority is checked again when saving.
- **Yours** contains your personal `my-<name>` servers. Only runs you start receive these,
including your runs in other conversations. Other people do not see your personal list.

Choose **Add or update**, enter a name and public HTTPS server URL, then enter the Bearer token
in the private modal. For an existing name, leave the token blank to keep it or enter a new token
to replace it. Stored tokens are never prefilled or revealed; the list shows only their last four
characters. Each scope holds up to five servers. OAuth-only servers use Composio instead.

Saving shows progress while the gateway checks the public address and your current access, then
returns to the existing MCPs page, including after repeated updates. Local field errors remain
inline. If a remote check fails or times out, **Try again** opens a fresh form; re-enter the token
because its draft was cleared. Timed-out checks do not save later. **Remove** asks for
confirmation and revokes that server's live relay access. New runs receive the saved list; Lean
runs receive no custom servers. Claude and Codex use the same scoped connections.

Admins can also manage these servers in the admin UI under the conversation's **Connections** or
the person's **Personal MCP servers**. Custom servers are separate from the engine inventory and
Cloud MCP selection controls below. Never paste a token into chat or a project MCP settings file.

## list_available_mcps

Discover the current engine’s host-configured MCP inventory.
Expand Down
Loading
Loading