(options: {
workspaceId: string;
projectPath: string;
runtime: Runtime;
workspacePath: string;
/** Whether repo-local MCP config is allowed for this project. */
trusted?: boolean;
/** Per-workspace MCP overrides (disabled servers, tool allowlists) */
overrides?: WorkspaceMCPOverrides;
/** Project secrets, used for resolving {secret: "KEY"} header references. */
projectSecrets?: Record<string, string>;
})
| 905 | } |
| 906 | |
| 907 | async getToolsForWorkspace(options: { |
| 908 | workspaceId: string; |
| 909 | projectPath: string; |
| 910 | runtime: Runtime; |
| 911 | workspacePath: string; |
| 912 | /** Whether repo-local MCP config is allowed for this project. */ |
| 913 | trusted?: boolean; |
| 914 | /** Per-workspace MCP overrides (disabled servers, tool allowlists) */ |
| 915 | overrides?: WorkspaceMCPOverrides; |
| 916 | /** Project secrets, used for resolving {secret: "KEY"} header references. */ |
| 917 | projectSecrets?: Record<string, string>; |
| 918 | }): Promise<MCPToolsForWorkspaceResult> { |
| 919 | const { |
| 920 | workspaceId, |
| 921 | projectPath, |
| 922 | runtime, |
| 923 | workspacePath, |
| 924 | trusted = false, |
| 925 | overrides, |
| 926 | projectSecrets, |
| 927 | } = options; |
| 928 | |
| 929 | // Fetch full server info for project-level allowlists and server filtering |
| 930 | const fullServerInfo = await this.getAllServers(projectPath, trusted); |
| 931 | |
| 932 | // Apply server-level overrides (enabled/disabled) before caching |
| 933 | const enabledServers = this.filterServersByPolicy( |
| 934 | this.applyServerOverrides(fullServerInfo, overrides) |
| 935 | ); |
| 936 | const enabledEntries = Object.entries(enabledServers).sort(([a], [b]) => a.localeCompare(b)); |
| 937 | |
| 938 | const enabledServerNames = new Set(enabledEntries.map(([name]) => name)); |
| 939 | |
| 940 | // Signature is based on *start config* only (not tool allowlists), so changing allowlists |
| 941 | // does not force a server restart. |
| 942 | const signatureEntries: Record<string, unknown> = {}; |
| 943 | for (const [name, info] of enabledEntries) { |
| 944 | if (info.transport === "stdio") { |
| 945 | signatureEntries[name] = { transport: "stdio", command: info.command }; |
| 946 | continue; |
| 947 | } |
| 948 | |
| 949 | // OAuth status affects whether we can attach authProvider during server start. |
| 950 | // Include this (redacted) information in the signature so we retry starting |
| 951 | // remote servers after a user logs in/out. |
| 952 | let hasOauthTokens = false; |
| 953 | if (this.mcpOauthService) { |
| 954 | try { |
| 955 | hasOauthTokens = await this.mcpOauthService.hasAuthTokens({ |
| 956 | serverUrl: info.url, |
| 957 | }); |
| 958 | } catch (error) { |
| 959 | log.debug("[MCP] Failed to resolve MCP OAuth status", { name, error }); |
| 960 | } |
| 961 | } |
| 962 | |
| 963 | try { |
| 964 | const { headers } = resolveHeaders(info.headers, projectSecrets); |
no test coverage detected