Skip to content

Commit f890f80

Browse files
committed
Resolve merge conflict by incorporating both suggestions
2 parents d3e1040 + 13ef8d0 commit f890f80

14 files changed

+552
-77
lines changed

CHANGELOG.md

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,33 @@
11
## 0.3.0
22

3+
## 0.2.2 (Aug 5, 2025)
4+
5+
FEATURES
6+
7+
* 2 New tools, get latest provider and module versions. See [#122](https://github.com/hashicorp/terraform-mcp-server/pull/122)
8+
9+
IMPROVEMENTS
10+
11+
* Restructure the codebase, changes too tool names from camelCase to snake_case. See [#118](https://github.com/hashicorp/terraform-mcp-server/pull/118)
12+
* Change tool names to be more consistent. See [#123](https://github.com/hashicorp/terraform-mcp-server/pull/123)
13+
14+
FIXES
15+
16+
* Enhanced provider documentation tool. See [#120](https://github.com/hashicorp/terraform-mcp-server/pull/120)
17+
* StreamableHttp endpoint customization, thanks to @sachinmalanki. See [#116](https://github.com/hashicorp/terraform-mcp-server/pull/116)
18+
19+
## 0.2.1 (July 11, 2025)
20+
21+
SECURITY
22+
23+
* Added support for CORS (strict, development, disabled), default mode is strict. See [#108](https://github.com/hashicorp/terraform-mcp-server/pull/108)
24+
* Added support for CORS allowed origins, default is empty. See [#108](https://github.com/hashicorp/terraform-mcp-server/pull/108)
25+
* Added support for stateless streamable HTTP mode, see [#108](https://github.com/hashicorp/terraform-mcp-server/pull/108)
26+
27+
IMPROVEMENTS
28+
29+
* Improved the HTTP retry to the registry. See [#109](https://github.com/hashicorp/terraform-mcp-server/pull/109)
30+
331
## 0.2.0 (July 3, 2025)
432

533
SECURITY

README.md

Lines changed: 21 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -143,12 +143,29 @@ The following sets of tools are available:
143143

144144
| Toolset | Tool | Description |
145145
|-------------|------------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
146-
| `providers` | `resolve_provider_doc_id` | Queries the Terraform Registry to find and list available documentation for a specific provider using the specified `service_slug`. Returns a list of provider document IDs with their titles and categories for resources, data sources, functions, or guides. |
147-
| `providers` | `get_provider_docs` | Fetches the complete documentation content for a specific provider resource, data source, or function using a document ID obtained from the `resolve_provider_doc_id` tool. Returns the raw documentation in markdown format. |
146+
| `providers` | `search_providers` | Queries the Terraform Registry to find and list available documentation for a specific provider using the specified `service_slug`. Returns a list of provider document IDs with their titles and categories for resources, data sources, functions, or guides. |
147+
| `providers` | `get_provider_details` | Fetches the complete documentation content for a specific provider resource, data source, or function using a document ID obtained from the `search_providers` tool. Returns the raw documentation in markdown format. |
148148
| `modules` | `search_modules` | Searches the Terraform Registry for modules based on specified `module_query` with pagination. Returns a list of module IDs with their names, descriptions, download counts, verification status, and publish dates |
149-
| `modules` | `module_details` | Retrieves detailed documentation for a module using a module ID obtained from the `search_modules` tool including inputs, outputs, configuration, submodules, and examples. |
149+
| `modules` | `get_module_details` | Retrieves detailed documentation for a module using a module ID obtained from the `search_modules` tool including inputs, outputs, configuration, submodules, and examples. |
150150
| `policies` | `search_policies` | Queries the Terraform Registry to find and list the appropriate Sentinel Policy based on the provided query `policy_query`. Returns a list of matching policies with terraform_policy_id(s) with their name, title and download counts. |
151-
| `policies` | `policy_details` | Retrieves detailed documentation for a policy set using a terraform_policy_id obtained from the `search_policies` tool including policy readme and implementation details. |
151+
| `policies` | `get_policy_details` | Retrieves detailed documentation for a policy set using a terraform_policy_id obtained from the `search_policies` tool including policy readme and implementation details. |
152+
153+
## Resource Configuration
154+
155+
### Available resources
156+
157+
| Resource URI | Description |
158+
|--------------|-------------|
159+
| `/terraform/style-guide` | Terraform Style Guide - Provides access to the official Terraform style guide documentation in markdown format |
160+
| `/terraform/module-development` | Terraform Module Development Guide - Comprehensive guide covering module composition, structure, providers, publishing, and refactoring best practices |
161+
162+
### Available Resource Templates
163+
164+
| Resouce Template URI | Description |
165+
|--------------|-------------|
166+
| `/terraform/providers/{namespace}/name/{name}/version/{version}` | Provider Resource Template - Dynamically retrieves detailed documentation and overview for any Terraform provider by namespace, name, and version |
167+
168+
152169
### Install from source
153170

154171
Use the latest release version:

e2e/e2e_test.go

Lines changed: 90 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -89,25 +89,25 @@ func runTestSuite(t *testing.T, client mcpClient.MCPClient, transportName string
8989
require.Equal(t, "terraform-mcp-server", result.ServerInfo.Name)
9090
})
9191

92-
for _, testCase := range providerTestCases {
93-
t.Run(fmt.Sprintf("%s_resolve_provider_doc_id/%s", transportName, testCase.TestName), func(t *testing.T) {
92+
for _, testCase := range searchProviderTestCases {
93+
t.Run(fmt.Sprintf("%s_search_providers/%s", transportName, testCase.TestName), func(t *testing.T) {
9494
ensureClientInitialized(t, client)
95-
t.Logf("TOOL resolve_provider_doc_id %s", testCase.TestDescription)
95+
t.Logf("TOOL search_providers %s", testCase.TestDescription)
9696
t.Logf("Test payload: %v", testCase.TestPayload)
9797

9898
ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
9999
defer cancel()
100100

101101
request := mcp.CallToolRequest{}
102-
request.Params.Name = "resolve_provider_doc_id"
102+
request.Params.Name = "search_providers"
103103
request.Params.Arguments = testCase.TestPayload
104104

105105
response, err := client.CallTool(ctx, request)
106106
if testCase.TestShouldFail {
107-
require.Error(t, err, "expected to call 'resolve_provider_doc_id' tool with error")
107+
require.Error(t, err, "expected to call 'search_providers' tool with error")
108108
t.Logf("Error: %v", err)
109109
} else {
110-
require.NoError(t, err, "expected to call 'resolve_provider_doc_id' tool successfully")
110+
require.NoError(t, err, "expected to call 'search_providers' tool successfully")
111111
require.False(t, response.IsError, "expected result not to be an error")
112112
require.Len(t, response.Content, 1, "expected content to have one item")
113113

@@ -129,25 +129,25 @@ func runTestSuite(t *testing.T, client mcpClient.MCPClient, transportName string
129129
})
130130
}
131131

132-
for _, testCase := range providerDocsTestCases {
133-
t.Run(fmt.Sprintf("%s_get_provider_docs/%s", transportName, testCase.TestName), func(t *testing.T) {
132+
for _, testCase := range providerDetailsTestCases {
133+
t.Run(fmt.Sprintf("%s_get_provider_details/%s", transportName, testCase.TestName), func(t *testing.T) {
134134
ensureClientInitialized(t, client)
135-
t.Logf("TOOL get_provider_docs %s", testCase.TestDescription)
135+
t.Logf("TOOL get_provider_details %s", testCase.TestDescription)
136136
t.Logf("Test payload: %v", testCase.TestPayload)
137137

138138
ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
139139
defer cancel()
140140

141141
request := mcp.CallToolRequest{}
142-
request.Params.Name = "get_provider_docs"
142+
request.Params.Name = "get_provider_details"
143143
request.Params.Arguments = testCase.TestPayload
144144

145145
response, err := client.CallTool(ctx, request)
146146
if testCase.TestShouldFail {
147-
require.Error(t, err, "expected to call 'get_provider_docs' tool with error")
147+
require.Error(t, err, "expected to call 'get_provider_details' tool with error")
148148
t.Logf("Error: %v", err)
149149
} else {
150-
require.NoError(t, err, "expected to call 'get_provider_docs' tool successfully")
150+
require.NoError(t, err, "expected to call 'get_provider_details' tool successfully")
151151
require.False(t, response.IsError, "expected result not to be an error")
152152
require.Len(t, response.Content, 1, "expected content to have one item")
153153

@@ -192,24 +192,24 @@ func runTestSuite(t *testing.T, client mcpClient.MCPClient, transportName string
192192
}
193193

194194
for _, testCase := range moduleDetailsTestCases {
195-
t.Run(fmt.Sprintf("%s_module_details/%s", transportName, testCase.TestName), func(t *testing.T) {
195+
t.Run(fmt.Sprintf("%s_get_module_details/%s", transportName, testCase.TestName), func(t *testing.T) {
196196
ensureClientInitialized(t, client)
197-
t.Logf("TOOL module_details %s", testCase.TestDescription)
197+
t.Logf("TOOL get_module_details %s", testCase.TestDescription)
198198
t.Logf("Test payload: %v", testCase.TestPayload)
199199

200200
ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
201201
defer cancel()
202202

203203
request := mcp.CallToolRequest{}
204-
request.Params.Name = "module_details"
204+
request.Params.Name = "get_module_details"
205205
request.Params.Arguments = testCase.TestPayload
206206

207207
response, err := client.CallTool(ctx, request)
208208
if testCase.TestShouldFail {
209-
require.Error(t, err, "expected to call 'module_details' tool with error")
209+
require.Error(t, err, "expected to call 'get_module_details' tool with error")
210210
t.Logf("Error: %v", err)
211211
} else {
212-
require.NoError(t, err, "expected to call 'module_details' tool successfully")
212+
require.NoError(t, err, "expected to call 'get_module_details' tool successfully")
213213
require.False(t, response.IsError, "expected result not to be an error")
214214
require.Len(t, response.Content, 1, "expected content to have one item")
215215

@@ -265,24 +265,24 @@ func runTestSuite(t *testing.T, client mcpClient.MCPClient, transportName string
265265
}
266266

267267
for _, testCase := range policyDetailsTestCases {
268-
t.Run("CallTool policy_details", func(t *testing.T) {
268+
t.Run("CallTool get_policy_details", func(t *testing.T) {
269269
// t.Parallel()
270-
t.Logf("TOOL policy_details %s", testCase.TestDescription)
270+
t.Logf("TOOL get_policy_details %s", testCase.TestDescription)
271271
t.Logf("Test payload: %v", testCase.TestPayload)
272272

273273
ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
274274
defer cancel()
275275

276276
request := mcp.CallToolRequest{}
277-
request.Params.Name = "policy_details"
277+
request.Params.Name = "get_policy_details"
278278
request.Params.Arguments = testCase.TestPayload
279279

280280
response, err := client.CallTool(ctx, request)
281281
if testCase.TestShouldFail {
282-
require.Error(t, err, "expected to call 'policy_details' tool with error")
282+
require.Error(t, err, "expected to call 'get_policy_details' tool with error")
283283
t.Logf("Error: %v", err)
284284
} else {
285-
require.NoError(t, err, "expected to call 'policy_details' tool successfully")
285+
require.NoError(t, err, "expected to call 'get_policy_details' tool successfully")
286286
require.False(t, response.IsError, "expected result not to be an error")
287287
require.Len(t, response.Content, 1, "expected content to have at least one item")
288288

@@ -297,6 +297,74 @@ func runTestSuite(t *testing.T, client mcpClient.MCPClient, transportName string
297297
})
298298
}
299299

300+
for _, testCase := range getLatestModuleVersionTestCases {
301+
t.Run(fmt.Sprintf("%s_get_latest_module_version/%s", transportName, testCase.TestName), func(t *testing.T) {
302+
ensureClientInitialized(t, client)
303+
t.Logf("TOOL get_latest_module_version %s", testCase.TestDescription)
304+
t.Logf("Test payload: %v", testCase.TestPayload)
305+
306+
ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
307+
defer cancel()
308+
309+
request := mcp.CallToolRequest{}
310+
request.Params.Name = "get_latest_module_version"
311+
request.Params.Arguments = testCase.TestPayload
312+
313+
response, err := client.CallTool(ctx, request)
314+
if testCase.TestShouldFail {
315+
require.Error(t, err, "expected to call 'get_latest_module_version' tool with error")
316+
t.Logf("Error: %v", err)
317+
} else {
318+
require.NoError(t, err, "expected to call 'get_latest_module_version' tool successfully")
319+
require.False(t, response.IsError, "expected result not to be an error")
320+
require.Len(t, response.Content, 1, "expected content to have one item")
321+
322+
textContent, ok := response.Content[0].(mcp.TextContent)
323+
require.True(t, ok, "expected content to be of type TextContent")
324+
t.Logf("Module version: %s", textContent.Text)
325+
326+
// Verify that the response contains a valid version string
327+
require.NotEmpty(t, textContent.Text, "expected version string to not be empty")
328+
// Basic version format validation (should contain at least one dot for semantic versioning)
329+
require.Contains(t, textContent.Text, ".", "expected version to contain at least one dot")
330+
}
331+
})
332+
}
333+
334+
for _, testCase := range getLatestProviderVersionTestCases {
335+
t.Run(fmt.Sprintf("%s_get_latest_provider_version/%s", transportName, testCase.TestName), func(t *testing.T) {
336+
ensureClientInitialized(t, client)
337+
t.Logf("TOOL get_latest_provider_version %s", testCase.TestDescription)
338+
t.Logf("Test payload: %v", testCase.TestPayload)
339+
340+
ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
341+
defer cancel()
342+
343+
request := mcp.CallToolRequest{}
344+
request.Params.Name = "get_latest_provider_version"
345+
request.Params.Arguments = testCase.TestPayload
346+
347+
response, err := client.CallTool(ctx, request)
348+
if testCase.TestShouldFail {
349+
require.Error(t, err, "expected to call 'get_latest_provider_version' tool with error")
350+
t.Logf("Error: %v", err)
351+
} else {
352+
require.NoError(t, err, "expected to call 'get_latest_provider_version' tool successfully")
353+
require.False(t, response.IsError, "expected result not to be an error")
354+
require.Len(t, response.Content, 1, "expected content to have one item")
355+
356+
textContent, ok := response.Content[0].(mcp.TextContent)
357+
require.True(t, ok, "expected content to be of type TextContent")
358+
t.Logf("Provider version: %s", textContent.Text)
359+
360+
// Verify that the response contains a valid version string
361+
require.NotEmpty(t, textContent.Text, "expected version string to not be empty")
362+
// Basic version format validation (should contain at least one dot for semantic versioning)
363+
require.Contains(t, textContent.Text, ".", "expected version to contain at least one dot")
364+
}
365+
})
366+
}
367+
300368
}
301369

302370
// createStdioClient creates a stdio-based MCP client

0 commit comments

Comments
 (0)