Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "@tableau/mcp-server",
"description": "An MCP server for Tableau, providing a suite of tools that will make it easier for developers to build AI applications that integrate with Tableau.",
"version": "1.14.6",
"version": "1.14.7",
"repository": {
"type": "git",
"url": "git+https://github.com/tableau/tableau-mcp.git"
Expand Down
220 changes: 17 additions & 203 deletions src/config.test.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
import { beforeEach, describe, expect, it, vi } from 'vitest';

import { exportedForTesting, ONE_HOUR_IN_MS, TEN_MINUTES_IN_MS } from './config.js';
import { stubDefaultEnvVars } from './testShared.js';

Expand Down Expand Up @@ -128,30 +126,6 @@ describe('Config', () => {
expect(config.maxRequestTimeoutMs).toBe(TEN_MINUTES_IN_MS);
});

it('should set disableQueryDatasourceValidationRequests to false by default', () => {
const config = new Config();
expect(config.disableQueryDatasourceValidationRequests).toBe(false);
});

it('should set disableQueryDatasourceValidationRequests to true when specified', () => {
vi.stubEnv('DISABLE_QUERY_DATASOURCE_VALIDATION_REQUESTS', 'true');

const config = new Config();
expect(config.disableQueryDatasourceValidationRequests).toBe(true);
});

it('should set disableMetadataApiRequests to false by default', () => {
const config = new Config();
expect(config.disableMetadataApiRequests).toBe(false);
});

it('should set disableMetadataApiRequests to true when specified', () => {
vi.stubEnv('DISABLE_METADATA_API_REQUESTS', 'true');

const config = new Config();
expect(config.disableMetadataApiRequests).toBe(true);
});

it('should set disableSessionManagement to false by default', () => {
const config = new Config();
expect(config.disableSessionManagement).toBe(false);
Expand Down Expand Up @@ -189,68 +163,28 @@ describe('Config', () => {
expect(config.tableauServerVersionCheckIntervalInHours).toBe(2);
});

describe('Tool filtering', () => {
it('should set empty arrays for includeTools and excludeTools when not specified', () => {
const config = new Config();
expect(config.includeTools).toEqual([]);
expect(config.excludeTools).toEqual([]);
});

it('should parse INCLUDE_TOOLS into an array of valid tool names', () => {
vi.stubEnv('INCLUDE_TOOLS', 'query-datasource,get-datasource-metadata');

const config = new Config();
expect(config.includeTools).toEqual(['query-datasource', 'get-datasource-metadata']);
});

it('should parse INCLUDE_TOOLS into an array of valid tool names when tool group names are used', () => {
vi.stubEnv('INCLUDE_TOOLS', 'query-datasource,workbook');

const config = new Config();
expect(config.includeTools).toEqual(['query-datasource', 'list-workbooks', 'get-workbook']);
});

it('should parse EXCLUDE_TOOLS into an array of valid tool names', () => {
vi.stubEnv('EXCLUDE_TOOLS', 'query-datasource');

const config = new Config();
expect(config.excludeTools).toEqual(['query-datasource']);
});

it('should parse EXCLUDE_TOOLS into an array of valid tool names when tool group names are used', () => {
vi.stubEnv('EXCLUDE_TOOLS', 'query-datasource,workbook');

const config = new Config();
expect(config.excludeTools).toEqual(['query-datasource', 'list-workbooks', 'get-workbook']);
});

it('should filter out invalid tool names from INCLUDE_TOOLS', () => {
vi.stubEnv('INCLUDE_TOOLS', 'query-datasource,order-hamburgers');

const config = new Config();
expect(config.includeTools).toEqual(['query-datasource']);
});

it('should filter out invalid tool names from EXCLUDE_TOOLS', () => {
vi.stubEnv('EXCLUDE_TOOLS', 'query-datasource,order-hamburgers');
it('should set mcpSiteSettingsCheckIntervalInMinutes to default when not specified', () => {
const config = new Config();
expect(config.mcpSiteSettingsCheckIntervalInMinutes).toBe(10);
});
Comment on lines +166 to +169
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Diff is kinda bad in this file. I moved the tests for overridable config to their own file and added tests for MCP_SITE_SETTINGS_CHECK_INTERVAL_IN_MINUTES and ENABLE_MCP_SITE_SETTINGS.


const config = new Config();
expect(config.excludeTools).toEqual(['query-datasource']);
});
it('should set mcpSiteSettingsCheckIntervalInMinutes to the specified value when specified', () => {
vi.stubEnv('MCP_SITE_SETTINGS_CHECK_INTERVAL_IN_MINUTES', '2');

it('should throw error when both INCLUDE_TOOLS and EXCLUDE_TOOLS are specified', () => {
vi.stubEnv('INCLUDE_TOOLS', 'query-datasource');
vi.stubEnv('EXCLUDE_TOOLS', 'get-datasource-metadata');
const config = new Config();
expect(config.mcpSiteSettingsCheckIntervalInMinutes).toBe(2);
});

expect(() => new Config()).toThrow('Cannot include and exclude tools simultaneously');
});
it('should set enableMcpSiteSettings to false by default', () => {
const config = new Config();
expect(config.enableMcpSiteSettings).toBe(false);
});

it('should throw error when both INCLUDE_TOOLS and EXCLUDE_TOOLS are specified with tool group names', () => {
vi.stubEnv('INCLUDE_TOOLS', 'datasource');
vi.stubEnv('EXCLUDE_TOOLS', 'workbook');
it('should set enableMcpSiteSettings to true when specified', () => {
vi.stubEnv('ENABLE_MCP_SITE_SETTINGS', 'true');

expect(() => new Config()).toThrow('Cannot include and exclude tools simultaneously');
});
const config = new Config();
expect(config.enableMcpSiteSettings).toBe(true);
});

describe('HTTP server config parsing', () => {
Expand Down Expand Up @@ -581,65 +515,6 @@ describe('Config', () => {
});
});

describe('Bounded context parsing', () => {
it('should set boundedContext to null sets when no project, datasource, or workbook IDs are provided', () => {
const config = new Config();
expect(config.boundedContext).toEqual({
projectIds: null,
datasourceIds: null,
workbookIds: null,
tags: null,
});
});

it('should set boundedContext to the specified tags and project, datasource, and workbook IDs when provided', () => {
vi.stubEnv('INCLUDE_PROJECT_IDS', ' 123, 456, 123 '); // spacing is intentional here to test trimming
vi.stubEnv('INCLUDE_DATASOURCE_IDS', '789,101');
vi.stubEnv('INCLUDE_WORKBOOK_IDS', '112,113');
vi.stubEnv('INCLUDE_TAGS', 'tag1,tag2');

const config = new Config();
expect(config.boundedContext).toEqual({
projectIds: new Set(['123', '456']),
datasourceIds: new Set(['789', '101']),
workbookIds: new Set(['112', '113']),
tags: new Set(['tag1', 'tag2']),
});
});

it('should throw error when INCLUDE_PROJECT_IDS is set to an empty string', () => {
vi.stubEnv('INCLUDE_PROJECT_IDS', '');

expect(() => new Config()).toThrow(
'When set, the environment variable INCLUDE_PROJECT_IDS must have at least one value',
);
});

it('should throw error when INCLUDE_DATASOURCE_IDS is set to an empty string', () => {
vi.stubEnv('INCLUDE_DATASOURCE_IDS', '');

expect(() => new Config()).toThrow(
'When set, the environment variable INCLUDE_DATASOURCE_IDS must have at least one value',
);
});

it('should throw error when INCLUDE_WORKBOOK_IDS is set to an empty string', () => {
vi.stubEnv('INCLUDE_WORKBOOK_IDS', '');

expect(() => new Config()).toThrow(
'When set, the environment variable INCLUDE_WORKBOOK_IDS must have at least one value',
);
});

it('should throw error when INCLUDE_TAGS is set to an empty string', () => {
vi.stubEnv('INCLUDE_TAGS', '');

expect(() => new Config()).toThrow(
'When set, the environment variable INCLUDE_TAGS must have at least one value',
);
});
});

describe('OAuth configuration', () => {
function stubDefaultOAuthEnvVars(): void {
vi.stubEnv('OAUTH_ISSUER', 'https://example.com');
Expand Down Expand Up @@ -971,65 +846,4 @@ describe('Config', () => {
expect(result).toBe(42);
});
});

describe('Max results limit parsing', () => {
it('should return null when MAX_RESULT_LIMIT and MAX_RESULT_LIMITS are not set', () => {
expect(new Config().getMaxResultLimit('query-datasource')).toBeNull();
});

it('should return the max result limit when MAX_RESULT_LIMITS has a single tool', () => {
vi.stubEnv('MAX_RESULT_LIMITS', 'query-datasource:100');

expect(new Config().getMaxResultLimit('query-datasource')).toEqual(100);
});

it('should return the max result limit when MAX_RESULT_LIMITS has a single tool group', () => {
vi.stubEnv('MAX_RESULT_LIMITS', 'datasource:200');

expect(new Config().getMaxResultLimit('query-datasource')).toEqual(200);
});

it('should return the max result limit for the tool when a tool and a tool group are both specified', () => {
vi.stubEnv('MAX_RESULT_LIMITS', 'query-datasource:100,datasource:200');

expect(new Config().getMaxResultLimit('query-datasource')).toEqual(100);
expect(new Config().getMaxResultLimit('list-datasources')).toEqual(200);
});

it('should fallback to MAX_RESULT_LIMIT when a tool-specific max result limit is not set', () => {
vi.stubEnv('MAX_RESULT_LIMITS', 'query-datasource:100');
vi.stubEnv('MAX_RESULT_LIMIT', '300');

expect(new Config().getMaxResultLimit('query-datasource')).toEqual(100);
expect(new Config().getMaxResultLimit('list-datasources')).toEqual(300);
});

it('should return null when MAX_RESULT_LIMITS has a non-number', () => {
vi.stubEnv('MAX_RESULT_LIMITS', 'query-datasource:abc');

const config = new Config();
expect(config.getMaxResultLimit('query-datasource')).toBe(null);
});

it('should return null when MAX_RESULT_LIMIT is specified as a non-number', () => {
vi.stubEnv('MAX_RESULT_LIMIT', 'abc');

const config = new Config();
expect(config.getMaxResultLimit('query-datasource')).toBe(null);
});

it('should return null when MAX_RESULT_LIMITS has a negative number', () => {
vi.stubEnv('MAX_RESULT_LIMITS', 'query-datasource:-100');

const config = new Config();
expect(config.getMaxResultLimit('query-datasource')).toBe(null);
});

it('should return null when MAX_RESULT_LIMIT is specified as a negative number', () => {
vi.stubEnv('MAX_RESULT_LIMIT', '-100');

const config = new Config();
expect(config.getMaxResultLimit('query-datasource')).toBe(null);
});
});
});
Loading