MCP Tool Scoping
How integration config, OAuth, and API keys control which tools are listed and executed
Integrate uses three independent layers to scope MCP tools. Understanding them prevents slow tool discovery and models seeing tools the user cannot use.
The three layers
| Layer | Mechanism | Controls |
|---|---|---|
| Integration config | Integrations passed to createMCPServer + X-Integrations header | Which tool definitions exist in your app |
| OAuth tokens | getProviderToken(provider, email, context) on each tools/call | Whether a tool executes for a user |
| API key | apiKey on server config → X-API-Key on MCP requests | Billing, rate limits, usage userId |
Listing is config-based by default: if Gmail is in your server config, Gmail tools appear even when the user has not connected Gmail. Execution fails without a token.
Connected-only tool loading
Use connectedOnly when building AI tools for a signed-in user so you only fetch schemas for integrations they have OAuth tokens for:
import { getVercelAITools } from "integrate-sdk/server";
import { serverClient } from "@/lib/integrate";
const tools = await getVercelAITools(serverClient, {
context: { userId: session.user.id },
connectedOnly: true,
mode: "code",
});This calls getProviderToken per configured integration (via your database adapter or callbacks) and only runs list_tools_by_integration for providers with usable tokens.
Manual integration filter
Pass an explicit allowlist when you already know which providers to expose:
const tools = await getVercelAITools(serverClient, {
integrationIds: ["github", "gmail", "slack"],
context: { userId },
});Or on the client directly:
const mcpTools = await serverClient.getEnabledToolsAsync({
integrationIds: ["github"],
context: { userId },
});listConnectedProviders helper
For custom logic (routing, UI badges, background jobs), use the exported helper:
import { listConnectedProviders } from "integrate-sdk/server";
const connected = await listConnectedProviders(
["github", "gmail", "slack"],
(provider, email, context) =>
serverClient.getProviderToken(provider, email, context),
{ userId: session.user.id }
);With a database adapter, getProviderToken is already wired. You can also derive connected providers from token rows using listConnectedProvidersFromRows.
Code Mode + scoping
Code Mode collapses hundreds of MCP tools into execute_code + get_types, which dramatically reduces context size. Pair it with connectedOnly so cold-start discovery only hits integrations the user has connected:
const tools = await getVercelAITools(serverClient, {
context: { userId },
connectedOnly: true,
mode: "code",
});Set codeMode.publicUrl or INTEGRATE_URL so Code Mode can call back into /api/integrate/mcp.
Triggers and automations
Scheduled work belongs in your application database, not the Integrate platform dashboard. Configure triggers callbacks or the database adapter in your app; the MCP scheduler calls your /api/integrate/triggers/notify route.
The hosted dashboard at integrate.dev is for API keys, billing, and usage, not for creating triggers on behalf of your users.
Related
- Performance: caching and concurrency
- Database adapters: token storage and
connectedOnly - Triggers: SDK trigger callbacks