Skip to content

Conversation

@ryoppippi
Copy link
Contributor

@ryoppippi ryoppippi commented Oct 9, 2025

Summary

Add support for fetching tools from multiple StackOne accounts via MCP.

Changes

API

  • Add setAccounts(accountIds: string[]) method to configure default account IDs
  • Add FetchToolsOptions.accountIds?: string[] to specify accounts per fetch call
  • Options override setAccounts() when both are provided

Behavior

  • Each account gets a separate MCP session with x-account-id header
  • Tools from all specified accounts are fetched in parallel and merged
  • Falls back to all available tools when no accounts specified

Testing

  • Mock MCP server now respects x-account-id header and returns account-specific tools
  • Test account-specific tool filtering
  • Test setAccounts() and fetchTools() override behavior
  • Verify tools from multiple accounts are correctly merged

Usage

const stackone = new StackOneToolSet({ apiKey: 'key' })

// Method 1: Use setAccounts
stackone.setAccounts(['acc1', 'acc2'])
const tools = await stackone.fetchTools()

// Method 2: Pass directly
const tools = await stackone.fetchTools({ 
  accountIds: ['acc1', 'acc2'] 
})

// Method 3: Override setAccounts
stackone.setAccounts(['acc1', 'acc2'])
const tools = await stackone.fetchTools({ 
  accountIds: ['acc3']  // Only acc3, ignores setAccounts
})

Implementation Notes

This follows the unified-cloud-api MCP endpoint design where:

  • 1 MCP session = 1 account (via x-account-id header)
  • Multiple accounts require multiple sessions
  • Different from /ai/chat/tools which accepts accountIds[] in body

Test Plan

  • All existing tests pass
  • New tests for multi-account filtering
  • Type checking passes
  • Mock server validates account-specific behavior

Summary by cubic

Add multi-account support to fetchTools so you can pull tools from multiple StackOne accounts in one call. Each account is fetched via MCP and merged into a single collection.

  • New Features
    • Added setAccounts(accountIds: string[]) to set default account IDs.
    • Added fetchTools({ accountIds }) to specify or override accounts per call.
    • Creates a separate MCP session per account using the x-account-id header and merges results.
    • Falls back to fetching all available tools when no accounts are specified.

Add support for fetching tools from multiple accounts via MCP:

- Add `setAccounts()` method to configure default account IDs
- Add `FetchToolsOptions.accountIds` to specify accounts per fetch
- Options override `setAccounts()` when both provided
- Each account gets separate MCP session with `x-account-id` header
- Tools from all accounts are merged into single collection

Tests:
- Mock MCP server now respects `x-account-id` header
- Test account-specific tool filtering
- Test `setAccounts()` and `fetchTools()` override behavior
- Verify tools from multiple accounts are correctly merged

Related: unified-cloud-api MCP endpoint uses x-account-id header
Copilot AI review requested due to automatic review settings October 9, 2025 13:01
@ryoppippi ryoppippi requested a review from a team as a code owner October 9, 2025 13:01
Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull Request Overview

This PR adds multi-account support to the StackOne toolset's fetchTools functionality. It enables fetching tools from multiple StackOne accounts simultaneously through the Model Context Protocol (MCP).

  • Implements setAccounts() method for setting default account IDs and FetchToolsOptions.accountIds for per-call account specification
  • Modifies the MCP session handling to use separate sessions per account with x-account-id headers
  • Updates the mock test server to handle account-specific tool filtering and adds comprehensive test coverage

Reviewed Changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 2 comments.

File Description
src/toolsets/stackone.ts Adds multi-account support with setAccounts() method and account-aware fetchTools() implementation
src/toolsets/tests/stackone.mcp-fetch.spec.ts Updates mock server for account-based tool filtering and adds comprehensive test suite for multi-account functionality

Tip: Customize your code reviews with copilot-instructions.md. Create the file or learn how to get started.

Comment on lines +144 to +158
const headers = { 'x-account-id': accountId };
const mergedHeaders = { ...this.headers, ...headers };

// Create a temporary toolset instance with the account-specific headers
const tempHeaders = mergedHeaders;
const originalHeaders = this.headers;
this.headers = tempHeaders;

try {
const tools = await super.fetchTools();
return tools.toArray();
} finally {
// Restore original headers
this.headers = originalHeaders;
}
Copy link

Copilot AI Oct 9, 2025

Choose a reason for hiding this comment

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

Modifying the instance's headers property during async operations creates a race condition. If multiple fetchTools calls run concurrently, they will overwrite each other's headers. Consider passing headers as a parameter to the parent method or creating a separate client instance for each account.

Suggested change
const headers = { 'x-account-id': accountId };
const mergedHeaders = { ...this.headers, ...headers };
// Create a temporary toolset instance with the account-specific headers
const tempHeaders = mergedHeaders;
const originalHeaders = this.headers;
this.headers = tempHeaders;
try {
const tools = await super.fetchTools();
return tools.toArray();
} finally {
// Restore original headers
this.headers = originalHeaders;
}
// Create a new instance of this toolset with account-specific headers
const accountHeaders = { ...this.headers, 'x-account-id': accountId };
// Clone the config for the new instance
const config = {
...this.config,
apiKey: this.config.apiKey,
accountId: accountId,
strict: this.config.strict,
removedParams: this.config.removedParams,
};
// Create a new instance of the current class (assume constructor signature is (config))
const ToolSetClass = this.constructor as typeof ToolSet;
const toolsetInstance = new ToolSetClass({
...config,
headers: accountHeaders,
});
const tools = await toolsetInstance.fetchTools();
return tools.toArray();

Copilot uses AI. Check for mistakes.
@@ -1,5 +1,5 @@
import { loadStackOneSpecs } from '../openapi/loader';
import { StackOneTool, type Tools } from '../tool';
import { StackOneTool, Tools } from '../tool';
Copy link

Copilot AI Oct 9, 2025

Choose a reason for hiding this comment

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

The import statement removes the 'type' keyword from Tools import. This changes Tools from a type-only import to a runtime import, which could affect bundle size and tree-shaking. Consider keeping it as 'type Tools' if Tools is only used for type annotations.

Suggested change
import { StackOneTool, Tools } from '../tool';
import { StackOneTool } from '../tool';
import type { Tools } from '../tool';

Copilot uses AI. Check for mistakes.
Copy link
Contributor

@NicolasBelissent NicolasBelissent left a comment

Choose a reason for hiding this comment

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

LGTM

_meta: undefined,
})
);
}
Copy link
Contributor

Choose a reason for hiding this comment

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

u ok with Mock?

Copy link
Contributor

@cubic-dev-ai cubic-dev-ai bot left a comment

Choose a reason for hiding this comment

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

1 issue found across 2 files

Prompt for AI agents (all 1 issues)

Understand the root cause of the following 1 issues and fix them.


<file name="src/toolsets/stackone.ts">

<violation number="1" location="src/toolsets/stackone.ts:150">
Running account fetches in parallel while mutating this.headers causes later iterations to overwrite the header used by earlier requests, so tools fetched for one account inherit another account&#39;s MCP session headers. Please avoid mutating shared headers during concurrent fetches (e.g., clone the toolset or run the fetches sequentially).</violation>
</file>

React with 👍 or 👎 to teach cubic. Mention @cubic-dev-ai to give feedback, ask questions, or re-run the review.

// Create a temporary toolset instance with the account-specific headers
const tempHeaders = mergedHeaders;
const originalHeaders = this.headers;
this.headers = tempHeaders;
Copy link
Contributor

@cubic-dev-ai cubic-dev-ai bot Oct 9, 2025

Choose a reason for hiding this comment

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

Running account fetches in parallel while mutating this.headers causes later iterations to overwrite the header used by earlier requests, so tools fetched for one account inherit another account's MCP session headers. Please avoid mutating shared headers during concurrent fetches (e.g., clone the toolset or run the fetches sequentially).

Prompt for AI agents
Address the following comment on src/toolsets/stackone.ts at line 150:

<comment>Running account fetches in parallel while mutating this.headers causes later iterations to overwrite the header used by earlier requests, so tools fetched for one account inherit another account&#39;s MCP session headers. Please avoid mutating shared headers during concurrent fetches (e.g., clone the toolset or run the fetches sequentially).</comment>

<file context>
@@ -103,6 +115,59 @@ export class StackOneToolSet extends ToolSet {
+        // Create a temporary toolset instance with the account-specific headers
+        const tempHeaders = mergedHeaders;
+        const originalHeaders = this.headers;
+        this.headers = tempHeaders;
+
+        try {
</file context>
Fix with Cubic

@pkg-pr-new
Copy link

pkg-pr-new bot commented Oct 9, 2025

Open in StackBlitz

npm i https://pkg.pr.new/StackOneHQ/stackone-ai-node/@stackone/ai@118

commit: 13fdcf8

@ryoppippi ryoppippi merged commit 926e625 into main Oct 9, 2025
6 checks passed
@ryoppippi ryoppippi deleted the accountID branch October 9, 2025 15:09
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.

2 participants