Skip to content
Scalekit Docs
Talk to an Engineer Dashboard

Connect AI agents to the Upstream MCP server

Vendor MCP
Open markdown

The Upstream MCP connector routes your AI agent's tool calls to Upstream's own MCP server through Scalekit. Each user connects their own Upstream access token once, and Scalekit sends it with every call, so your agent never handles credentials. It comes with 46 tools.

Tools
46
What they doRead · write · destructive
20 · 22 · 420 read22 write4 destructive
Users sign in with

Setup

  1. Install the SDK

    Terminal window
    npm install @scalekit-sdk/node dotenv
  2. Set your credentials

    Add your Scalekit credentials to your .env file. Find values in app.scalekit.com > Developers > API Credentials.

    .env
    SCALEKIT_ENVIRONMENT_URL=<your-environment-url>
    SCALEKIT_CLIENT_ID=<your-client-id>
    SCALEKIT_CLIENT_SECRET=<your-client-secret>
  3. Create the Upstream MCP connection

    In AgentKit > Connections, create an Upstream MCP connection. The name you give it is the connection_name your code passes. See Configure connections.

    Console steps with screenshots

    Register your Upstream Personal Access Token with Scalekit so it can authenticate and proxy inbox requests on behalf of your users. Upstream MCP uses bearer token authentication.

    1. Get a bearer token

      • Sign in to your Upstream account and open your account settings.

      • Find the Personal Access Tokens (or API tokens) section.

      • Click Create token. Give it a name (e.g., Agent Auth) and confirm.

      • Copy the token immediately — it will not be shown again.

    2. Create a connection in Scalekit

      • In Scalekit dashboard, go to AgentKit > Connections. Find Upstream and click Create.

      • Note the Connection name — you will use this as connection_name in your code (e.g., upstreammcp).

    3. Add a connected account

      Connected accounts link a specific user identifier in your system to an Upstream Personal Access Token. Add one in the dashboard to test. In production, each user adds their own through the authorization link: they enter their credentials on the page it opens.

      In the dashboard, to test

      • Open the connection you created and click the Connected Accounts tab → Add account.

      • Fill in:

        • Your User’s ID — a unique identifier for this user in your system (e.g., user_123)
        • Bearer Token — the Personal Access Token you copied in step 1
      • Click Save.

      From your backend, if your app already has the credentials

      For example, when users enter them on a settings page in your app:

      import { ConnectorStatus } from '@scalekit-sdk/node'
      const authorizationDetails = {
      details: {
      case: 'staticAuth',
      value: { details: { token: 'upstream_pat_...' } },
      },
      };
      let { connectedAccount } = await scalekit.actions.upsertConnectedAccount({
      connectionName: 'upstreammcp',
      identifier: 'user_123', // your user's unique ID
      authorizationDetails,
      });
      // Make sure the account is ACTIVE before the first tool call.
      if (connectedAccount?.status !== ConnectorStatus.ACTIVE) {
      ({ connectedAccount } = await scalekit.actions.upsertConnectedAccount({
      connectionName: 'upstreammcp',
      identifier: 'user_123',
      authorizationDetails,
      }));
      }
  4. Authorize a user and make your first call

    quickstart.mts
    import { ScalekitClient } from '@scalekit-sdk/node'
    import 'dotenv/config'
    import { createInterface } from 'node:readline/promises'
    const scalekit = new ScalekitClient(
    process.env.SCALEKIT_ENVIRONMENT_URL,
    process.env.SCALEKIT_CLIENT_ID,
    process.env.SCALEKIT_CLIENT_SECRET,
    )
    const actions = scalekit.actions
    const connector = 'upstreammcp'
    const identifier = 'user_123'
    // Generate an authorization link for the user
    const { link } = await actions.getAuthorizationLink({ connectionName: connector, identifier })
    console.log('Authorize Upstream MCP:', link)
    const rl = createInterface({ input: process.stdin, output: process.stdout })
    await rl.question('Press Enter after authorizing...')
    rl.close()
    // Make your first call
    const result = await actions.executeTool({
    connector,
    identifier,
    toolName: 'upstreammcp_get_inbox_split_threads',
    toolInput: {},
    })
    console.log(result)
    Terminal window
    npx tsx quickstart.mts

    Each user opens the link once and enters their Upstream MCP server credentials there. If your app already has a user's credentials, add the account from your backend instead, as the console steps above show. See Authorize a user for the full flow and statuses.

Tools

Pass the exact name to execute_tool
Try in PlaygroundRequest a tool
  • upstreammcp_get_channel_threadsList threads in a specific channel.Read-only

    Get Channel Threads

    List threads in a specific channel. Use list-channels first to get channel IDs. Returns paginated thread list with subjects, senders, and dates.

    Inputs

    channelIdstringrequired
    Channel ID from list-channels
    limitinteger
    Max results, 1-50 (default 20)
    pageTokenstring
    Cursor for next page from previous response
  • upstreammcp_get_inbox_split_threadsList threads in a specific inbox split, category, or system split.Read-only

    Get Inbox Split Threads

    List threads in a specific inbox split, category, or system split. Provide exactly one of splitId, category, or systemSplit. Get valid filter values from list-inbox-splits first.

    Inputs

    categorystring
    Inbox category (e.g. CATEGORY_PRIMARY)one of CATEGORY_PRIMARYCATEGORY_PERSONALCATEGORY_PROMOTIONSCATEGORY_SOCIALCATEGORY_UPDATESCATEGORY_FORUMS
    limitinteger
    Page size, 1-100 (default 20)
    pageTokenstring
    Cursor for next page from previous response
    splitIdstring
    Custom inbox split ID from list-inbox-splits
    systemSplitstring
    System split: "follow-ups", "needs-reply", or "cold-emails"one of follow-upsneeds-replycold-emailsfollowupsautodrafts
  • upstreammcp_get_label_threadsList threads with a specific label.Read-only

    Get Label Threads

    List threads with a specific label. Use list-labels first to get label IDs. Returns paginated thread list.

    Inputs

    labelIdstringrequired
    Label ID from list-labels
    limitinteger
    Max results, 1-50 (default 20)
    pageTokenstring
    Cursor for next page from previous response
  • upstreammcp_get_selfGet current user profile, account status, settings, and organization memberships in one call.Read-only

    Get Current User

    Get current user profile, account status, settings, and organization memberships in one call. Useful for understanding user identity, feature flags, unread counts, and AI draft configuration.

    Inputs

    This tool takes no inputs.

  • upstreammcp_list_channelsList all channels the user belongs to.Read-only

    List Channels

    List all channels the user belongs to. Channels are team shared spaces for organizing threads. Returns channel IDs, names, colors, member counts, and unread counts. Use get-channel-threads to browse threads within a channel.

    Inputs

    This tool takes no inputs.

  • upstreammcp_list_contactsList all contacts the user has interacted with.Read-only

    List Contacts

    List all contacts the user has interacted with. Returns email addresses, display names, and profile pictures. Useful for finding recipient addresses when composing or replying to emails.

    Inputs

    This tool takes no inputs.

  • upstreammcp_list_draft_threadsList new thread drafts saved through MCP.Read-only

    List Draft Threads

    List new thread drafts saved through MCP. Returns unsent draft threads owned by the user.

    Inputs

    limitinteger
    Max results, 1-50 (default 20)
    pageTokenstring
    Cursor for next page from previous response
  • upstreammcp_list_inbox_splitsList all inbox splits visible to the authenticated user, in display order.Read-only

    ListInboxSplits

    List all inbox splits visible to the authenticated user, in display order. Includes Primary, system splits (Needs Reply, Follow Ups), custom splits, and category splits (Promotions, Social, Updates). Each entry contains a filter_param object — pass it directly to get-inbox-split-threads. Use this tool first to discover available splits before fetching threads.

    Inputs

    This tool takes no inputs.

Workflows

Tool calling
  • Use upstreammcp_search_inbox to find threads by keyword across subject, body, and sender before reading them.
  • Use upstreammcp_get_inbox_split_threads with systemSplit: "needs-reply" to list threads that are waiting on the user.
  • Use upstreammcp_read_thread to load a thread’s messages — the default concise format saves context tokens.
  • Use upstreammcp_generate_draft to get an AI-drafted reply for a thread, then upstreammcp_reply_to_thread to send it.
  • Use upstreammcp_done_thread to archive a thread once it is handled.
const toolResponse = await actions.executeTool({
connector: 'upstreammcp',
identifier: 'user_123',
toolName: 'upstreammcp_search_inbox',
toolInput: {
query: 'invoice',
limit: 10,
},
});
console.log('Matching threads:', toolResponse.data);