Skip to content

Commit 2f39d90

Browse files
authored
fix: refactor MCP tools to use class-based initialization [DX-579] (#263)
* Refactor MCP tools to use ContentfulMcpTools for initialization and environment configuration. Removed dotenv and related environment validation from config files. Updated tool registration to utilize new client configuration methods. Enhanced error handling across various tools for better reliability. * chore: update package lock * chore: update env file local mcp * chore: refactor tests * chore: fix linter
1 parent 041a0f6 commit 2f39d90

File tree

164 files changed

+3925
-2489
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

164 files changed

+3925
-2489
lines changed

package-lock.json

Lines changed: 663 additions & 69 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

packages/mcp-server/package.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,8 @@
4444
},
4545
"dependencies": {
4646
"@contentful/mcp-tools": "0.1.1",
47-
"@modelcontextprotocol/sdk": "^1.17.0"
47+
"@modelcontextprotocol/sdk": "^1.17.0",
48+
"dotenv": "^17.2.3"
4849
},
4950
"devDependencies": {
5051
"@types/node": "^22.16.5",

packages/mcp-tools/src/config/env.ts renamed to packages/mcp-server/src/config/env.ts

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
1-
import dotenv from 'dotenv';
21
import { z } from 'zod';
2+
import dotenv from 'dotenv';
33
dotenv.config();
44

55
const EnvSchema = z.object({
@@ -11,7 +11,6 @@ const EnvSchema = z.object({
1111
.optional()
1212
.default('api.contentful.com')
1313
.describe('Contentful API host'),
14-
1514
APP_ID: z.string().optional().describe('Contentful App ID'),
1615
SPACE_ID: z.string().optional().describe('Contentful Space ID'),
1716
ENVIRONMENT_ID: z

packages/mcp-server/src/tools/register.test.ts

Lines changed: 48 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,23 @@
11
import { describe, it, expect, vi, beforeEach } from 'vitest';
22
import { registerAllTools } from './register.js';
33
import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
4-
import {
5-
aiActionTools,
6-
assetTools,
7-
contentTypeTools,
8-
contextTools,
9-
editorInterfaceTools,
10-
entryTools,
11-
environmentTools,
12-
jobTools,
13-
localeTools,
14-
orgTools,
15-
spaceTools,
16-
tagTools,
17-
taxonomyTools,
18-
} from '@contentful/mcp-tools';
4+
import { ContentfulMcpTools } from '@contentful/mcp-tools';
5+
import { env } from '../config/env.js';
6+
7+
// Mock the env module
8+
vi.mock('../config/env.js', () => ({
9+
env: {
10+
success: true,
11+
data: {
12+
CONTENTFUL_MANAGEMENT_ACCESS_TOKEN: 'test-token',
13+
CONTENTFUL_HOST: 'api.contentful.com',
14+
SPACE_ID: 'test-space-id',
15+
ENVIRONMENT_ID: 'master',
16+
ORGANIZATION_ID: 'test-org-id',
17+
APP_ID: 'test-app-id',
18+
},
19+
},
20+
}));
1921

2022
describe('registerAllTools', () => {
2123
let mockServer: McpServer;
@@ -38,20 +40,30 @@ describe('registerAllTools', () => {
3840
it('should register all standard tool collections', () => {
3941
registerAllTools(mockServer);
4042

43+
// Create a ContentfulMcpTools instance to count tools
44+
const mcpTools = new ContentfulMcpTools({
45+
accessToken: env.data!.CONTENTFUL_MANAGEMENT_ACCESS_TOKEN,
46+
host: env.data!.CONTENTFUL_HOST,
47+
spaceId: env.data!.SPACE_ID,
48+
environmentId: env.data!.ENVIRONMENT_ID,
49+
organizationId: env.data!.ORGANIZATION_ID,
50+
appId: env.data!.APP_ID,
51+
});
52+
4153
// Count expected tool registrations from standard collections
4254
const standardToolCollections = [
43-
aiActionTools,
44-
assetTools,
45-
contentTypeTools,
46-
contextTools,
47-
editorInterfaceTools,
48-
entryTools,
49-
environmentTools,
50-
localeTools,
51-
orgTools,
52-
spaceTools,
53-
tagTools,
54-
taxonomyTools,
55+
mcpTools.getAiActionTools(),
56+
mcpTools.getAssetTools(),
57+
mcpTools.getContentTypeTools(),
58+
mcpTools.getContextTools(),
59+
mcpTools.getEditorInterfaceTools(),
60+
mcpTools.getEntryTools(),
61+
mcpTools.getEnvironmentTools(),
62+
mcpTools.getLocaleTools(),
63+
mcpTools.getOrgTools(),
64+
mcpTools.getSpaceTools(),
65+
mcpTools.getTagTools(),
66+
mcpTools.getTaxonomyTools(),
5567
];
5668

5769
const expectedStandardToolCount = standardToolCollections.reduce(
@@ -90,7 +102,15 @@ describe('registerAllTools', () => {
90102
it('should register spaceToSpaceMigrationHandler with workflow tools', () => {
91103
registerAllTools(mockServer);
92104

93-
const handlerConfig = jobTools.spaceToSpaceMigrationHandler;
105+
const mcpTools = new ContentfulMcpTools({
106+
accessToken: env.data!.CONTENTFUL_MANAGEMENT_ACCESS_TOKEN,
107+
host: env.data!.CONTENTFUL_HOST,
108+
spaceId: env.data!.SPACE_ID,
109+
environmentId: env.data!.ENVIRONMENT_ID,
110+
organizationId: env.data!.ORGANIZATION_ID,
111+
appId: env.data!.APP_ID,
112+
});
113+
const handlerConfig = mcpTools.getJobTools().spaceToSpaceMigrationHandler;
94114

95115
// Find the call for the migration handler
96116
const handlerCall = registerToolSpy.mock.calls.find(

packages/mcp-server/src/tools/register.ts

Lines changed: 31 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,6 @@
11
import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
2-
import {
3-
aiActionTools,
4-
assetTools,
5-
contentTypeTools,
6-
contextTools,
7-
editorInterfaceTools,
8-
entryTools,
9-
environmentTools,
10-
jobTools,
11-
localeTools,
12-
orgTools,
13-
spaceTools,
14-
tagTools,
15-
taxonomyTools,
16-
} from '@contentful/mcp-tools';
2+
import { ContentfulMcpTools } from '@contentful/mcp-tools';
3+
import { env } from '../config/env.js';
174

185
/**
196
* Registers all Contentful MCP tools with the server.
@@ -24,6 +11,34 @@ import {
2411
* - The migration handler controls their enable/disable state
2512
*/
2613
export function registerAllTools(server: McpServer): void {
14+
if (!env.success || !env.data) {
15+
throw new Error('Environment variables are not properly configured');
16+
}
17+
18+
// Initialize tools with configuration from environment variables
19+
const tools = new ContentfulMcpTools({
20+
accessToken: env.data.CONTENTFUL_MANAGEMENT_ACCESS_TOKEN,
21+
host: env.data.CONTENTFUL_HOST,
22+
spaceId: env.data.SPACE_ID,
23+
environmentId: env.data.ENVIRONMENT_ID,
24+
organizationId: env.data.ORGANIZATION_ID,
25+
appId: env.data.APP_ID,
26+
});
27+
28+
// Get tool collections
29+
const aiActionTools = tools.getAiActionTools();
30+
const assetTools = tools.getAssetTools();
31+
const contentTypeTools = tools.getContentTypeTools();
32+
const contextTools = tools.getContextTools();
33+
const editorInterfaceTools = tools.getEditorInterfaceTools();
34+
const entryTools = tools.getEntryTools();
35+
const environmentTools = tools.getEnvironmentTools();
36+
const localeTools = tools.getLocaleTools();
37+
const orgTools = tools.getOrgTools();
38+
const spaceTools = tools.getSpaceTools();
39+
const tagTools = tools.getTagTools();
40+
const taxonomyTools = tools.getTaxonomyTools();
41+
2742
// Combine standard tool collections
2843
const allToolCollections = [
2944
aiActionTools,
@@ -54,9 +69,7 @@ export function registerAllTools(server: McpServer): void {
5469
);
5570
});
5671
});
57-
58-
// Handle migration tools specially
59-
// These tools should be disabled by default and controlled by the handler
72+
const jobTools = tools.getJobTools();
6073
const workflowToolsToDisable = [
6174
'spaceToSpaceParamCollection',
6275
'exportSpace',

packages/mcp-tools/package.json

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,6 @@
4444
"contentful-export": "^7.21.78",
4545
"contentful-import": "^9.4.111",
4646
"contentful-management": "^11.54.3",
47-
"dotenv": "^16.4.7",
4847
"fast-xml-parser": "^5.2.5",
4948
"outdent": "^0.8.0",
5049
"@contentful/rich-text-types": "^16.8.5",
Lines changed: 135 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,135 @@
1+
import type { ContentfulConfig } from './config/types.js';
2+
import { createAiActionTools } from './tools/ai-actions/register.js';
3+
import { createAssetTools } from './tools/assets/register.js';
4+
import { createContentTypeTools } from './tools/content-types/register.js';
5+
import { createContextTools } from './tools/context/register.js';
6+
import { createEditorInterfaceTools } from './tools/editor-interfaces/register.js';
7+
import { createEntryTools } from './tools/entries/register.js';
8+
import { createEnvironmentTools } from './tools/environments/register.js';
9+
import { createLocaleTools } from './tools/locales/register.js';
10+
import { createOrgTools } from './tools/orgs/register.js';
11+
import { createSpaceTools } from './tools/spaces/register.js';
12+
import { createTagTools } from './tools/tags/register.js';
13+
import { createTaxonomyTools } from './tools/taxonomies/register.js';
14+
import { createJobTools } from './tools/jobs/space-to-space-migration/register.js';
15+
16+
/**
17+
* Main class for Contentful MCP Tools
18+
*
19+
* This class encapsulates all Contentful MCP tools and provides methods
20+
* to access tool collections. Configuration is passed to the constructor
21+
* and can be updated at runtime.
22+
*/
23+
export class ContentfulMcpTools {
24+
private config: ContentfulConfig;
25+
26+
constructor(config: ContentfulConfig) {
27+
// Set defaults
28+
this.config = {
29+
...config,
30+
host: config.host ?? 'api.contentful.com',
31+
environmentId: config.environmentId ?? 'master',
32+
};
33+
}
34+
35+
/**
36+
* Update the configuration after initialization
37+
*
38+
* @param updates - Partial configuration to merge with existing config
39+
*/
40+
updateConfig(updates: Partial<ContentfulConfig>): void {
41+
this.config = { ...this.config, ...updates };
42+
}
43+
44+
/**
45+
* Get AI action tools
46+
*/
47+
getAiActionTools() {
48+
return createAiActionTools(this.config);
49+
}
50+
51+
/**
52+
* Get asset tools
53+
*/
54+
getAssetTools() {
55+
return createAssetTools(this.config);
56+
}
57+
58+
/**
59+
* Get content type tools
60+
*/
61+
getContentTypeTools() {
62+
return createContentTypeTools(this.config);
63+
}
64+
65+
/**
66+
* Get context tools
67+
*/
68+
getContextTools() {
69+
return createContextTools(this.config);
70+
}
71+
72+
/**
73+
* Get editor interface tools
74+
*/
75+
getEditorInterfaceTools() {
76+
return createEditorInterfaceTools(this.config);
77+
}
78+
79+
/**
80+
* Get entry tools
81+
*/
82+
getEntryTools() {
83+
return createEntryTools(this.config);
84+
}
85+
86+
/**
87+
* Get environment tools
88+
*/
89+
getEnvironmentTools() {
90+
return createEnvironmentTools(this.config);
91+
}
92+
93+
/**
94+
* Get locale tools
95+
*/
96+
getLocaleTools() {
97+
return createLocaleTools(this.config);
98+
}
99+
100+
/**
101+
* Get organization tools
102+
*/
103+
getOrgTools() {
104+
return createOrgTools(this.config);
105+
}
106+
107+
/**
108+
* Get space tools
109+
*/
110+
getSpaceTools() {
111+
return createSpaceTools(this.config);
112+
}
113+
114+
/**
115+
* Get tag tools
116+
*/
117+
getTagTools() {
118+
return createTagTools(this.config);
119+
}
120+
121+
/**
122+
* Get taxonomy tools
123+
*/
124+
getTaxonomyTools() {
125+
return createTaxonomyTools(this.config);
126+
}
127+
128+
/**
129+
* Get job tools (space-to-space migration)
130+
*/
131+
getJobTools() {
132+
return createJobTools(this.config);
133+
}
134+
}
135+

packages/mcp-tools/src/config/contentful.ts

Lines changed: 0 additions & 27 deletions
This file was deleted.
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
/**
2+
* Configuration for Contentful MCP Tools
3+
*/
4+
export interface ContentfulConfig {
5+
/** Contentful CMA (Content Management API) access token */
6+
accessToken: string;
7+
/** Contentful API host (default: 'api.contentful.com') */
8+
host?: string;
9+
/** Contentful Space ID */
10+
spaceId?: string;
11+
/** Contentful Environment ID (default: 'master') */
12+
environmentId?: string;
13+
/** Contentful Organization ID */
14+
organizationId?: string;
15+
/** Contentful App ID */
16+
appId?: string;
17+
}

0 commit comments

Comments
 (0)