Skip to content

feat(toolsets): add multi-account support for fetchTools - #118

Merged
ryoppippi merged 2 commits into
mainfrom
accountID
Oct 9, 2025
Merged

feat(toolsets): add multi-account support for fetchTools#118
ryoppippi merged 2 commits into
mainfrom
accountID

Conversation

@ryoppippi

@ryoppippiryoppippi commented Oct 9, 2025

Copy link
Copy Markdown
Contributor

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

conststackone=newStackOneToolSet({apiKey: 'key'})// Method 1: Use setAccountsstackone.setAccounts(['acc1','acc2'])consttools=awaitstackone.fetchTools()// Method 2: Pass directlyconsttools=awaitstackone.fetchTools({accountIds: ['acc1','acc2']})// Method 3: Override setAccountsstackone.setAccounts(['acc1','acc2'])consttools=awaitstackone.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
CopilotAI review requested due to automatic review settings October 9, 2025 13:01
@ryoppippi
ryoppippi requested a review from a team as a code ownerOctober 9, 2025 13:01

CopilotAI left a comment

Copy link
Copy Markdown
Contributor

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.

FileDescription
src/toolsets/stackone.tsAdds multi-account support with setAccounts() method and account-aware fetchTools() implementation
src/toolsets/tests/stackone.mcp-fetch.spec.tsUpdates 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;
}

CopilotAIOct 9, 2025

Copy link

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
constheaders={'x-account-id': accountId};
constmergedHeaders={ ...this.headers, ...headers};
// Create a temporary toolset instance with the account-specific headers
consttempHeaders=mergedHeaders;
constoriginalHeaders=this.headers;
this.headers=tempHeaders;
try{
consttools=awaitsuper.fetchTools();
returntools.toArray();
}finally{
// Restore original headers
this.headers=originalHeaders;
}
// Create a new instance of this toolset with account-specific headers
constaccountHeaders={ ...this.headers,'x-account-id': accountId};
// Clone the config for the new instance
constconfig={
...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))
constToolSetClass=this.constructorastypeofToolSet;
consttoolsetInstance=newToolSetClass({
...config,
headers: accountHeaders,
});
consttools=awaittoolsetInstance.fetchTools();
returntools.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';

CopilotAIOct 9, 2025

Copy link

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';
importtype{Tools}from'../tool';

Copilot uses AI. Check for mistakes.

@NicolasBelissentNicolasBelissent left a comment

Copy link
Copy Markdown
Contributor

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
Copy Markdown
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?

@cubic-dev-aicubic-dev-aiBot left a comment

Copy link
Copy Markdown
Contributor

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;

@cubic-dev-aicubic-dev-aiBotOct 9, 2025

Copy link
Copy Markdown
Contributor

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

pkg-pr-newBot commented Oct 9, 2025

Copy link
Copy Markdown

Open in StackBlitz

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

commit: 13fdcf8

@ryoppippi
ryoppippi merged commit 926e625 into mainOct 9, 2025
6 checks passed
@ryoppippi
ryoppippi deleted the accountID branch October 9, 2025 15:09
@github-actionsgithub-actionsBot mentioned this pull request Oct 9, 2025
Sign up for freeto 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.

3 participants

@ryoppippi@NicolasBelissent