Skip to content

Scope integrations.list to the active toolkit policy#1241

Merged
RhysSullivan merged 1 commit into
mainfrom
scope-integrations-list-to-toolkit
Jul 1, 2026
Merged

Scope integrations.list to the active toolkit policy#1241
RhysSullivan merged 1 commit into
mainfrom
scope-integrations-list-to-toolkit

Conversation

@RhysSullivan

Copy link
Copy Markdown
Collaborator

Problem

A toolkit-scoped MCP session could still see the full workspace provider catalog through integrations.list, including providers the toolkit grants no tools from. tools.list, tools.search, and connections.list were already narrowed to the toolkit's granted surface, but integrations.list was not.

The result: a provider the toolkit blocks entirely (zero granted tools) still showed up in the catalog as a "provider with 0 tools" — both through the integrations.list core tool an agent can call over MCP and through the console "Tool providers" view. That is a small capability leak (the scoped agent learns which providers exist elsewhere) and noise.

Fix

Scope integrationsList by the active tool policy provider, mirroring the existing connectionsList behavior:

  • No active policy provider (the workspace console): return the full catalog, unchanged.
  • Active policy provider (a toolkit session): keep static system sources, and keep DB-backed providers only when they contribute at least one visible tool under the policy.

Both executor.integrations.list and the integrations.list core tool route through this one function, so the MCP surface and the console are fixed together.

Testing

Added an e2e scenario (scenarios/toolkits-mcp.test.ts) that drives the real toolkit MCP endpoint:

  • Two providers exist in the workspace; a toolkit grants only one (plus the executor.coreTools.* introspection namespace), not the other.
  • The unscoped workspace endpoint lists both providers.
  • The toolkit endpoint's integrations.list lists the granted provider and excludes the ungranted one, which also exposes no callable tools.

Verified both ways: with the fix reverted the scenario fails (the ungranted provider appears in the toolkit catalog); with the fix it passes. All toolkit MCP scenarios pass on selfhost.

A toolkit-scoped MCP session listed the full workspace provider catalog
through integrations.list, including providers the toolkit grants no tools
from. tools.list and connections.list were already narrowed to the
toolkit's granted surface, but integrations.list was not, so a provider
with zero granted tools still surfaced in the catalog (via the
integrations.list core tool and the console "Tool providers" view).

Filter integrationsList by the active tool policy provider, mirroring
connectionsList: static system sources stay, and DB-backed providers are
kept only when they contribute at least one visible tool under the policy.
Behavior is unchanged when no policy provider is active (the workspace
console still lists the full catalog).

Covered by an e2e scenario that drives the real toolkit MCP endpoint and
asserts a provider the toolkit grants no tools is absent from the catalog
while still callable providers remain.
@cloudflare-workers-and-pages

cloudflare-workers-and-pages Bot commented Jul 1, 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 Updated (UTC)
✅ Deployment successful!
View logs
executor-cloud e8ed3f0 Jul 01 2026, 08:08 PM

@cloudflare-workers-and-pages

cloudflare-workers-and-pages Bot commented Jul 1, 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
executor-marketing e8ed3f0 Commit Preview URL

Branch Preview URL
Jul 01 2026, 08:07 PM

@RhysSullivan
RhysSullivan merged commit fd16b2b into main Jul 1, 2026
12 of 14 checks passed
@github-actions

github-actions Bot commented Jul 1, 2026

Copy link
Copy Markdown
Contributor

Cloudflare preview

Torn down — the PR is closed.

@greptile-apps

greptile-apps Bot commented Jul 1, 2026

Copy link
Copy Markdown

Greptile Summary

This PR scopes integrationsList to the active toolkit tool-policy provider, so a toolkit session no longer leaks provider names it grants zero tools from. The fix mirrors the existing connectionsList filtering pattern: when activeToolPolicyProvider is set, it calls toolsList to collect visible integration slugs and strips any DB-backed provider that contributes nothing under the active policy; static system sources are always kept.

  • executor.ts: integrationsList now calls toolsList({ includeAnnotations: false }) under a scoped session and builds a Set of visible slugs to filter dbIntegrations, exactly as connectionsList already does.
  • toolkits-mcp.test.ts: a new e2e scenario stands up two providers, grants only one to the toolkit, then asserts the ungranted provider is absent from integrations.list and exposes no callable tools under tools.search.

Confidence Score: 4/5

Safe to merge. The core change is a small, focused addition that correctly mirrors an already-proven pattern, and the e2e scenario validates both the positive and negative cases end-to-end.

The executor.ts change is tight and correct — it reuses the exact same toolsList + slug-set filtering already used by connectionsList, so the logic is well-precedented. The only gap is in the new test scenario: the Effect.ensuring finalizer removes the toolkit but leaves the two created integrations and connections behind, which accumulates orphaned rows on a shared selfhost instance over many runs. That is a test hygiene concern, not a product defect.

The finalizer in e2e/scenarios/toolkits-mcp.test.ts (around line 691) should also call the integrations and connections removal APIs to match the cleanup contract described in e2e/AGENTS.md.

Important Files Changed

Filename Overview
packages/core/sdk/src/executor.ts Adds toolkit-policy scoping to integrationsList, mirroring the existing connectionsList pattern: calls toolsList to collect visible integration slugs and filters out DB-backed providers with zero visible tools. Logic is correct and consistent with the rest of the file.
e2e/scenarios/toolkits-mcp.test.ts New scenario correctly tests positive (granted provider visible) and negative (ungranted provider hidden) cases, plus verifies the exclusion is real access control via tools.search. Cleanup finalizer omits the created integrations and connections, leaving orphaned rows on selfhost.

Flowchart

%%{init: {'theme': 'neutral'}}%%
flowchart TD
    A[integrationsList called] --> B{activeToolPolicyProvider set?}
    B -- No --> C[Return full catalog:\nstaticIntegrations + dbIntegrations]
    B -- Yes --> D[Call toolsList with includeAnnotations=false]
    D --> E[Filter: non-static tools only]
    E --> F[Build Set of visibleIntegrationSlugs]
    F --> G[Filter dbIntegrations\nby slug in visibleIntegrationSlugs]
    G --> H[Return staticIntegrations + filtered dbIntegrations]
Loading
%%{init: {'theme': 'base', 'themeVariables': {"darkMode": true, "background": "#0d1117", "primaryColor": "#21262d", "primaryTextColor": "#e6edf3", "primaryBorderColor": "#8b949e", "lineColor": "#8b949e", "textColor": "#e6edf3", "edgeLabelBackground": "#161b22", "actorBkg": "#21262d", "actorBorder": "#8b949e", "actorTextColor": "#e6edf3", "actorLineColor": "#8b949e", "signalColor": "#8b949e", "signalTextColor": "#e6edf3", "noteBkgColor": "#373320", "noteBorderColor": "#d4a72c", "noteTextColor": "#f0e6c0", "labelBoxBkgColor": "#21262d", "labelBoxBorderColor": "#8b949e", "labelTextColor": "#e6edf3", "loopTextColor": "#e6edf3", "activationBkgColor": "#30363d", "activationBorderColor": "#8b949e"}}}%%
flowchart TD
    A[integrationsList called] --> B{activeToolPolicyProvider set?}
    B -- No --> C[Return full catalog:\nstaticIntegrations + dbIntegrations]
    B -- Yes --> D[Call toolsList with includeAnnotations=false]
    D --> E[Filter: non-static tools only]
    E --> F[Build Set of visibleIntegrationSlugs]
    F --> G[Filter dbIntegrations\nby slug in visibleIntegrationSlugs]
    G --> H[Return staticIntegrations + filtered dbIntegrations]
Loading

Reviews (1): Last reviewed commit: "Scope integrations.list to the active to..." | Re-trigger Greptile

Comment on lines +691 to +700
Effect.ensuring(
Effect.gen(function* () {
const listed = yield* client.toolkits.list();
yield* Effect.forEach(
listed.toolkits.filter((toolkit) => toolkit.name === toolkitName),
(toolkit) => client.toolkits.remove({ params: { toolkitId: toolkit.id } }),
{ discard: true },
);
}).pipe(Effect.ignore),
),

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Incomplete cleanup in Effect.ensuring

The finalizer removes the toolkit but leaves the two created integrations (granted, ungranted) and their connections behind. Per e2e/AGENTS.md, all resources created in a scenario must be torn down by Effect.ensuring — a mid-test failure must not leak into the shared selfhost instance. On selfhost every scenario shares the bootstrap admin, so these orphaned integrations accumulate across runs. The unique() prefix prevents collision on slug counts, but the leaked rows still grow the global catalog over time.

Context Used: e2e/AGENTS.md (source)

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