|
1 |
| -// Copyright (c) HashiCorp, Inc. |
2 |
| -// SPDX-License-Identifier: MPL-2.0 |
3 |
| - |
4 | 1 | package tfregistry
|
5 | 2 |
|
6 | 3 | import (
|
7 | 4 | "context"
|
8 | 5 | "fmt"
|
| 6 | + "io" |
9 | 7 | "net/http"
|
10 | 8 |
|
11 | 9 | "github.com/mark3labs/mcp-go/mcp"
|
12 | 10 | "github.com/mark3labs/mcp-go/server"
|
13 | 11 | log "github.com/sirupsen/logrus"
|
14 | 12 | )
|
15 | 13 |
|
| 14 | +// Base URL for the Terraform style guide and module development guide markdown files |
| 15 | +const terraformGuideRawURL = "https://raw.githubusercontent.com/hashicorp/web-unified-docs/main/content/terraform/v1.12.x/docs/language" |
| 16 | + |
| 17 | +// RegisterResources adds the new resource |
16 | 18 | func RegisterResources(hcServer *server.MCPServer, registryClient *http.Client, logger *log.Logger) {
|
17 |
| - // Add resources for official and partner providers |
18 |
| - hcServer.AddResource(ProviderResource(registryClient, fmt.Sprintf("%sproviders/official", PROVIDER_BASE_PATH), "Official Providers list", "official", logger)) |
19 |
| - hcServer.AddResource(ProviderResource(registryClient, fmt.Sprintf("%sproviders/partner", PROVIDER_BASE_PATH), "Partner Providers list", "partner", logger)) |
| 19 | + hcServer.AddResource(TerraformStyleGuideResource(registryClient, logger)) |
| 20 | + hcServer.AddResource(TerraformModuleDevGuideResource(registryClient, logger)) |
20 | 21 | }
|
21 | 22 |
|
22 |
| -func ProviderResource(registryClient *http.Client, resourceURI string, description string, providerType string, logger *log.Logger) (mcp.Resource, server.ResourceHandlerFunc) { |
| 23 | +// TerraformStyleGuideResource returns the resource and handler for the style guide |
| 24 | +func TerraformStyleGuideResource(httpClient *http.Client, logger *log.Logger) (mcp.Resource, server.ResourceHandlerFunc) { |
| 25 | + resourceURI := "/terraform/style-guide" |
| 26 | + description := "Terraform Style Guide" |
| 27 | + |
23 | 28 | return mcp.NewResource(
|
24 | 29 | resourceURI,
|
25 | 30 | description,
|
26 | 31 | mcp.WithMIMEType("text/markdown"),
|
27 | 32 | mcp.WithResourceDescription(description),
|
28 |
| - // TODO: Add pagination parameters here using the correct mcp-go mechanism |
29 |
| - // Example (conceptual): |
30 |
| - // mcp.WithInteger("page_number", mcp.Description("Page number"), mcp.Optional()), |
31 |
| - // mcp.WithInteger("page_size", mcp.Description("Page size"), mcp.Optional()), |
32 | 33 | ),
|
33 | 34 | func(ctx context.Context, request mcp.ReadResourceRequest) ([]mcp.ResourceContents, error) {
|
34 |
| - listOfProviders, err := GetProviderList(registryClient, providerType, logger) |
| 35 | + resp, err := httpClient.Get(fmt.Sprintf("%s/style.mdx", terraformGuideRawURL)) |
| 36 | + if err != nil { |
| 37 | + return nil, logAndReturnError(logger, "Error fetching Terraform Style Guide markdown", err) |
| 38 | + } |
| 39 | + defer resp.Body.Close() |
| 40 | + if resp.StatusCode != http.StatusOK { |
| 41 | + return nil, logAndReturnError(logger, "Non-200 response fetching Terraform Style Guide markdown", fmt.Errorf("status: %s", resp.Status)) |
| 42 | + } |
| 43 | + body, err := io.ReadAll(resp.Body) |
35 | 44 | if err != nil {
|
36 |
| - return nil, logAndReturnError(logger, fmt.Sprintf("Provider Resource: error getting %s provider list", providerType), err) |
| 45 | + return nil, logAndReturnError(logger, "Error reading Terraform Style Guide markdown", err) |
37 | 46 | }
|
38 |
| - resourceContents := make([]mcp.ResourceContents, len(listOfProviders)) |
39 |
| - for i, provider := range listOfProviders { |
40 |
| - namespace, name, version := ExtractProviderNameAndVersion(fmt.Sprintf("%s/%s/name/%s/version/latest", PROVIDER_BASE_PATH, provider["namespace"], provider["name"])) |
41 |
| - logger.Debugf("Extracted namespace: %s, name: %s, version: %s", namespace, name, version) |
| 47 | + return []mcp.ResourceContents{ |
| 48 | + mcp.TextResourceContents{ |
| 49 | + MIMEType: "text/markdown", |
| 50 | + URI: resourceURI, |
| 51 | + Text: string(body), |
| 52 | + }, |
| 53 | + }, nil |
| 54 | + } |
| 55 | +} |
42 | 56 |
|
43 |
| - versionNumber, err := GetLatestProviderVersion(registryClient, namespace, name, logger) |
44 |
| - if err != nil { |
45 |
| - return nil, logAndReturnError(logger, fmt.Sprintf("Provider Resource: error getting %s/%s provider version %s", namespace, name, versionNumber), err) |
46 |
| - } |
| 57 | +// TerraformModuleDevGuideResource returns a resource and handler for the Terraform Module Development Guide markdown files |
| 58 | +func TerraformModuleDevGuideResource(httpClient *http.Client, logger *log.Logger) (mcp.Resource, server.ResourceHandlerFunc) { |
| 59 | + resourceURI := "/terraform/module-development" |
| 60 | + description := "Terraform Module Development Guide" |
47 | 61 |
|
48 |
| - providerVersionUri := fmt.Sprintf("%s/%s/name/%s/version/%s", PROVIDER_BASE_PATH, namespace, name, versionNumber) |
49 |
| - logger.Debugf("Provider resource - providerVersionUri: %s", providerVersionUri) |
| 62 | + var urls = []struct { |
| 63 | + Name string |
| 64 | + URL string |
| 65 | + }{ |
| 66 | + {"index", fmt.Sprintf("%s/modules/develop/index.mdx", terraformGuideRawURL)}, |
| 67 | + {"composition", fmt.Sprintf("%s/modules/develop/composition.mdx", terraformGuideRawURL)}, |
| 68 | + {"structure", fmt.Sprintf("%s/modules/develop/structure.mdx", terraformGuideRawURL)}, |
| 69 | + {"providers", fmt.Sprintf("%s/modules/develop/providers.mdx", terraformGuideRawURL)}, |
| 70 | + {"publish", fmt.Sprintf("%s/modules/develop/publish.mdx", terraformGuideRawURL)}, |
| 71 | + {"refactoring", fmt.Sprintf("%s/modules/develop/refactoring.mdx", terraformGuideRawURL)}, |
| 72 | + } |
50 | 73 |
|
51 |
| - providerDocs, err := ProviderResourceTemplateHandler(registryClient, providerVersionUri, logger) |
| 74 | + return mcp.NewResource( |
| 75 | + resourceURI, |
| 76 | + description, |
| 77 | + mcp.WithMIMEType("text/markdown"), |
| 78 | + mcp.WithResourceDescription(description), |
| 79 | + ), |
| 80 | + func(ctx context.Context, request mcp.ReadResourceRequest) ([]mcp.ResourceContents, error) { |
| 81 | + var contents []mcp.ResourceContents |
| 82 | + for _, u := range urls { |
| 83 | + resp, err := httpClient.Get(u.URL) |
52 | 84 | if err != nil {
|
53 |
| - return nil, logAndReturnError(logger, fmt.Sprintf("Provider Resource: error with provider template handler %s/%s provider version %s details", namespace, name, versionNumber), err) |
| 85 | + return nil, logAndReturnError(logger, fmt.Sprintf("Error fetching %s markdown", u.Name), err) |
54 | 86 | }
|
55 |
| - resourceContents[i] = mcp.TextResourceContents{ |
56 |
| - MIMEType: "text/markdown", |
57 |
| - URI: providerVersionUri, |
58 |
| - Text: fmt.Sprintf("# %s Provider \n\n %s", provider["name"], providerDocs), |
| 87 | + if resp.StatusCode != http.StatusOK { |
| 88 | + resp.Body.Close() |
| 89 | + return nil, logAndReturnError(logger, fmt.Sprintf("Non-200 response fetching %s markdown", u.Name), fmt.Errorf("status: %s", resp.Status)) |
59 | 90 | }
|
| 91 | + body, err := io.ReadAll(resp.Body) |
| 92 | + resp.Body.Close() |
| 93 | + if err != nil { |
| 94 | + return nil, logAndReturnError(logger, fmt.Sprintf("Error reading %s markdown", u.Name), err) |
| 95 | + } |
| 96 | + contents = append(contents, mcp.TextResourceContents{ |
| 97 | + MIMEType: "text/markdown", |
| 98 | + URI: fmt.Sprintf("%s/%s", resourceURI, u.Name), |
| 99 | + Text: string(body), |
| 100 | + }) |
60 | 101 | }
|
61 |
| - return resourceContents, nil |
| 102 | + return contents, nil |
62 | 103 | }
|
63 | 104 | }
|
0 commit comments