{"_id":"@aetherwing/fcp-terraform","_rev":"3-8166fed133e9a937c179e892e1e87468","name":"@aetherwing/fcp-terraform","dist-tags":{"latest":"0.1.2"},"versions":{"0.1.0":{"name":"@aetherwing/fcp-terraform","version":"0.1.0","keywords":["terraform","hcl","infrastructure","mcp","fcp"],"license":"MIT","_id":"@aetherwing/fcp-terraform@0.1.0","maintainers":[{"name":"aetherwing-admin","email":"scott@aetherwing.io"}],"homepage":"https://github.com/aetherwing-io/fcp-terraform#readme","bugs":{"url":"https://github.com/aetherwing-io/fcp-terraform/issues"},"dist":{"shasum":"983a7bb92eb205a7dbb38271ca0fab669223981e","tarball":"https://registry.npmjs.org/@aetherwing/fcp-terraform/-/fcp-terraform-0.1.0.tgz","fileCount":19,"integrity":"sha512-ceQmkDSQ5XRRdNSLl4EdUcX4fwbLWF0qo9YrUpllYC9qw8Upg+UTMFW99j46XL0curgGZLpg7s/FcuUXcA3jtw==","signatures":[{"sig":"MEQCIEpImGNJDYxd3Jy2f8xJf6uQ0ccWnZSwufOLFpEy9M4vAiBEcdj5rKiHHuv1CN+PL4tFIyb/EBGfIx4UP1hGqRMOYA==","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":71345},"main":"dist/index.js","type":"module","types":"dist/index.d.ts","engines":{"node":">=22"},"exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js"}},"gitHead":"e373902fb6e04a83299ad103056cd128a640b0ac","scripts":{"test":"vitest run","build":"tsc"},"_npmUser":{"name":"aetherwing-admin","email":"scott@aetherwing.io"},"repository":{"url":"git+https://github.com/aetherwing-io/fcp-terraform.git","type":"git"},"_npmVersion":"11.6.0","description":"MCP server for Terraform HCL generation through intent-level commands","directories":{},"_nodeVersion":"24.7.0","dependencies":{"@aetherwing/fcp-core":"^0.1.0","@modelcontextprotocol/sdk":"^1.27.1"},"_hasShrinkwrap":false,"devDependencies":{"vitest":"^3.1.1","typescript":"^5.8.3","@types/node":"^22.15.2","@cdktf/hcl2json":"^0.21.0"},"_npmOperationalInternal":{"tmp":"tmp/fcp-terraform_0.1.0_1772226001306_0.8426580636780705","host":"s3://npm-registry-packages-npm-production"}},"0.1.1":{"name":"@aetherwing/fcp-terraform","version":"0.1.1","keywords":["terraform","hcl","infrastructure","mcp","fcp"],"license":"MIT","_id":"@aetherwing/fcp-terraform@0.1.1","maintainers":[{"name":"aetherwing-admin","email":"scott@aetherwing.io"}],"homepage":"https://github.com/aetherwing-io/fcp-terraform#readme","bugs":{"url":"https://github.com/aetherwing-io/fcp-terraform/issues"},"dist":{"shasum":"7af2702b22d965eb140b2f466436a13506391d1f","tarball":"https://registry.npmjs.org/@aetherwing/fcp-terraform/-/fcp-terraform-0.1.1.tgz","fileCount":19,"integrity":"sha512-dKAwWTyIRjpSSQCjA6VbhyMEFh2pVTAxhB0jfWXOkXfjTTCfnbeRdf63nTLTOXUjbJngIgZBhmBko1pfzJM+4Q==","signatures":[{"sig":"MEYCIQCLPx4K/oAbGZWODDsBc/T1biDmYa/ZeED9dgGdhKhK5AIhAMTog9gejrojjeMT+ly1Qe/jD77eUILjso43WEv2LGfB","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@aetherwing%2ffcp-terraform@0.1.1","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"unpackedSize":71345},"main":"dist/index.js","type":"module","types":"dist/index.d.ts","engines":{"node":">=22"},"exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js"}},"gitHead":"39b4bdae88762c0a60e8d3cdd6c5eefe33f01a09","scripts":{"test":"vitest run","build":"tsc"},"_npmUser":{"name":"GitHub Actions","email":"npm-oidc-no-reply@github.com","trustedPublisher":{"id":"github","oidcConfigId":"oidc:87bcb42b-eb77-4fbb-8227-4a6b1178a987"}},"repository":{"url":"git+https://github.com/aetherwing-io/fcp-terraform.git","type":"git"},"_npmVersion":"11.8.0","description":"MCP server for Terraform HCL generation through intent-level commands","directories":{},"_nodeVersion":"24.13.1","dependencies":{"@aetherwing/fcp-core":"^0.1.0","@modelcontextprotocol/sdk":"^1.27.1"},"_hasShrinkwrap":false,"devDependencies":{"vitest":"^3.1.1","typescript":"^5.8.3","@types/node":"^22.15.2","@cdktf/hcl2json":"^0.21.0"},"_npmOperationalInternal":{"tmp":"tmp/fcp-terraform_0.1.1_1772226680985_0.46091521454594386","host":"s3://npm-registry-packages-npm-production"}},"0.1.2":{"name":"@aetherwing/fcp-terraform","version":"0.1.2","description":"MCP server for Terraform HCL generation through intent-level commands","type":"module","main":"dist/index.js","types":"dist/index.d.ts","exports":{".":{"import":"./dist/index.js","types":"./dist/index.d.ts"}},"scripts":{"build":"tsc","test":"vitest run"},"dependencies":{"@aetherwing/fcp-core":"^0.1.0","@modelcontextprotocol/sdk":"^1.27.1"},"devDependencies":{"@cdktf/hcl2json":"^0.21.0","@types/node":"^22.15.2","typescript":"^5.8.3","vitest":"^3.1.1"},"engines":{"node":">=22"},"license":"MIT","repository":{"type":"git","url":"git+https://github.com/aetherwing-io/fcp-terraform.git"},"keywords":["terraform","hcl","infrastructure","mcp","fcp"],"gitHead":"d1494ed58634ed176d21575e30b01f75403751b8","_id":"@aetherwing/fcp-terraform@0.1.2","bugs":{"url":"https://github.com/aetherwing-io/fcp-terraform/issues"},"homepage":"https://github.com/aetherwing-io/fcp-terraform#readme","_nodeVersion":"24.13.1","_npmVersion":"11.8.0","dist":{"integrity":"sha512-SCxWO4sap3hEPOqE9gfeASLy5kOhMHLBGF0xMg5uFhyuYUQgzzaMcaXmZ9yzUYcP7ZIJlKE9WdD0xY9ufm3pNA==","shasum":"1d674284462ab76d1188fa22a8d86bd2f565900c","tarball":"https://registry.npmjs.org/@aetherwing/fcp-terraform/-/fcp-terraform-0.1.2.tgz","fileCount":19,"unpackedSize":72891,"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@aetherwing%2ffcp-terraform@0.1.2","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEYCIQD/w0vdM6Xa59nOL8hEjNwD7FY0XLEt7JjzfNkLkHK1MAIhAMnDeNPtdUV+1ZKIp7QaVrbv4tcLDZfJBDWw1cCjUg20"}]},"_npmUser":{"name":"GitHub Actions","email":"npm-oidc-no-reply@github.com","trustedPublisher":{"id":"github","oidcConfigId":"oidc:87bcb42b-eb77-4fbb-8227-4a6b1178a987"}},"directories":{},"maintainers":[{"name":"aetherwing-admin","email":"scott@aetherwing.io"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/fcp-terraform_0.1.2_1772235867391_0.5019630123423209"},"_hasShrinkwrap":false}},"time":{"created":"2026-02-27T21:00:01.218Z","modified":"2026-02-27T23:44:27.828Z","0.1.0":"2026-02-27T21:00:01.500Z","0.1.1":"2026-02-27T21:11:21.156Z","0.1.2":"2026-02-27T23:44:27.551Z"},"bugs":{"url":"https://github.com/aetherwing-io/fcp-terraform/issues"},"license":"MIT","homepage":"https://github.com/aetherwing-io/fcp-terraform#readme","keywords":["terraform","hcl","infrastructure","mcp","fcp"],"repository":{"type":"git","url":"git+https://github.com/aetherwing-io/fcp-terraform.git"},"description":"MCP server for Terraform HCL generation through intent-level commands","maintainers":[{"name":"aetherwing-admin","email":"scott@aetherwing.io"}],"readme":"# fcp-terraform\n\nMCP server for Terraform HCL generation through intent-level commands.\n\n## What It Does\n\nfcp-terraform lets LLMs build Terraform configurations by describing infrastructure intent -- resources, data sources, variables, outputs -- and renders them into valid HCL. Instead of writing raw HCL syntax, the LLM sends operations like `add resource aws_instance web ami:\"ami-0c55b159\" instance_type:t2.micro` and fcp-terraform manages the semantic model, dependency graph, and serialization. Built on the [FCP](https://github.com/aetherwing-io/fcp) framework.\n\n## Quick Example\n\n```\nterraform_session('new \"Main Infrastructure\"')\n\nterraform([\n  'add resource aws_instance web ami:\"ami-0c55b159\" instance_type:t2.micro',\n  'add resource aws_s3_bucket assets bucket:\"my-assets\"',\n  'add variable region default:\"us-east-1\" type:string',\n  'add output instance_ip value:\"aws_instance.web.public_ip\"',\n])\n\nterraform_query('plan')\n```\n\nThe `plan` query produces:\n\n```hcl\nvariable \"region\" {\n  type    = string\n  default = \"us-east-1\"\n}\n\nresource \"aws_instance\" \"web\" {\n  ami           = \"ami-0c55b159\"\n  instance_type = \"t2.micro\"\n}\n\nresource \"aws_s3_bucket\" \"assets\" {\n  bucket = \"my-assets\"\n}\n\noutput \"instance_ip\" {\n  value = aws_instance.web.public_ip\n}\n```\n\n### Available MCP Tools\n\n| Tool | Purpose |\n|------|---------|\n| `terraform(ops)` | Batch mutations -- add, set, remove, connect, nest, label, style |\n| `terraform_query(q)` | Inspect the config -- map, list, describe, plan, graph, validate, find |\n| `terraform_session(action)` | Lifecycle -- new, open, save, checkpoint, undo, redo |\n| `terraform_help()` | Full reference card |\n\n### Supported Block Types\n\n| Verb | Syntax |\n|------|--------|\n| `add resource` | `add resource TYPE LABEL [key:value...]` |\n| `add provider` | `add provider PROVIDER [region:R] [key:value...]` |\n| `add variable` | `add variable NAME [type:T] [default:V] [description:D]` |\n| `add output` | `add output NAME value:EXPR [description:D]` |\n| `add data` | `add data TYPE LABEL [key:value...]` |\n| `add module` | `add module LABEL source:PATH [key:value...]` |\n| `connect` | `connect SRC -> TGT [label:TEXT]` |\n| `set` | `set LABEL key:value [key:value...]` |\n| `nest` | `nest LABEL BLOCK_TYPE [key:value...]` |\n| `remove` | `remove LABEL` or `remove @SELECTOR` |\n\n### Selectors\n\n```\n@type:aws_instance      All resources of a given type\n@provider:aws            All blocks from a given provider\n@kind:resource           All blocks of kind (resource, variable, output, data)\n@tag:KEY or @tag:KEY=VAL Blocks matching a tag\n@all                     All blocks\n```\n\n## Installation\n\nRequires Node >= 18.\n\n```bash\nnpm install @aetherwing/fcp-terraform\n```\n\n### MCP Client Configuration\n\n```json\n{\n  \"mcpServers\": {\n    \"terraform\": {\n      \"command\": \"node\",\n      \"args\": [\"node_modules/@aetherwing/fcp-terraform/dist/index.js\"]\n    }\n  }\n}\n```\n\n## Architecture\n\n3-layer architecture:\n\n```\nMCP Server (Intent Layer)\n  src/server/ -- Parses op strings, resolves refs, dispatches\n        |\nSemantic Model (Domain)\n  src/model/ -- In-memory Terraform graph (resources, data, variables, outputs)\n  src/types/ -- Core TypeScript interfaces\n        |\nSerialization (HCL)\n  src/hcl.ts -- Semantic model -> HCL text output\n```\n\nSupporting modules:\n\n- `src/ops.ts` -- Operation string parser\n- `src/verbs.ts` -- Verb specs and reference card\n- `src/queries.ts` -- Query dispatcher (map, plan, graph, validate, etc.)\n- `src/adapter.ts` -- FCP core adapter\n\nProvider is auto-detected from resource type prefixes (`aws_`, `google_`, `azurerm_`).\n\n## Worked Example: AWS Production Web Stack\n\nA realistic deployment showing how operations compose. This example creates a VPC with subnets, EC2, RDS, S3, IAM, and security groups -- 13 resources total.\n\n```\nterraform_session('new \"Acme Corp Web Stack\"')\n\n# Provider + Variables\nterraform([\n  'add provider aws region:us-east-1',\n  'add variable environment type:string default:\"production\" description:\"Deployment environment\"',\n  'add variable project_name type:string default:\"acme-web\" description:\"Project name\"',\n  'add variable vpc_cidr type:string default:\"10.0.0.0/16\" description:\"VPC CIDR block\"',\n  'add variable instance_type type:string default:\"t3.medium\" description:\"EC2 instance type\"',\n  'add variable db_instance_class type:string default:\"db.t3.medium\" description:\"RDS instance class\"',\n])\n\n# Networking\nterraform([\n  'add resource aws_vpc main cidr_block:var.vpc_cidr enable_dns_support:true enable_dns_hostnames:true',\n  'add resource aws_subnet public_a vpc_id:aws_vpc.main.id cidr_block:\"10.0.1.0/24\" map_public_ip_on_launch:true',\n  'add resource aws_subnet public_b vpc_id:aws_vpc.main.id cidr_block:\"10.0.2.0/24\" map_public_ip_on_launch:true',\n  'add resource aws_internet_gateway igw vpc_id:aws_vpc.main.id',\n  'add resource aws_route_table public vpc_id:aws_vpc.main.id',\n])\n\n# Nested blocks for route table and security groups\nterraform([\n  'nest public route cidr_block:\"0.0.0.0/0\" gateway_id:aws_internet_gateway.igw.id',\n  'add resource aws_security_group web name:\"${var.project_name}-web-sg\" vpc_id:aws_vpc.main.id',\n  'nest web ingress from_port:80 to_port:80 protocol:\"tcp\"',\n  'nest web ingress from_port:443 to_port:443 protocol:\"tcp\"',\n  'nest web egress from_port:0 to_port:0 protocol:\"-1\"',\n])\n\n# Compute + Database\nterraform([\n  'add resource aws_instance webserver ami:\"ami-0c55b159\" instance_type:var.instance_type subnet_id:aws_subnet.public_a.id',\n  'nest webserver root_block_device volume_size:20 volume_type:\"gp3\"',\n  'add resource aws_db_subnet_group dbsubnet name:\"${var.project_name}-db-subnet\"',\n  'set dbsubnet subnet_ids:\"[aws_subnet.public_a.id,aws_subnet.public_b.id]\"',\n  'add resource aws_db_instance rds engine:\"postgresql\" instance_class:var.db_instance_class allocated_storage:20',\n  'add resource aws_s3_bucket assets bucket:\"${var.project_name}-assets-${var.environment}\"',\n])\n\n# Outputs\nterraform([\n  'add output vpc_id value:aws_vpc.main.id',\n  'add output web_ip value:aws_instance.webserver.public_ip',\n  'add output db_endpoint value:aws_db_instance.rds.endpoint',\n])\n\n# Tags (all resources at once)\nterraform([\n  'style main tags:\"Name=${var.project_name}-vpc,Environment=${var.environment}\"',\n  'style public_a tags:\"Name=${var.project_name}-public-a,Environment=${var.environment}\"',\n  'style public_b tags:\"Name=${var.project_name}-public-b,Environment=${var.environment}\"',\n  'style igw tags:\"Name=${var.project_name}-igw,Environment=${var.environment}\"',\n  'style webserver tags:\"Name=${var.project_name}-web,Environment=${var.environment}\"',\n  'style rds tags:\"Name=${var.project_name}-db,Environment=${var.environment}\"',\n  'style assets tags:\"Name=${var.project_name}-assets,Environment=${var.environment}\"',\n])\n\n# Day-2 modifications\nterraform([\n  'label webserver app_server',                         # Rename\n  'set instance_type default:\"t3.large\"',               # Change default\n  'remove assets',                                       # Replace S3 bucket\n  'add resource aws_s3_bucket static_assets bucket:\"${var.project_name}-static-${var.environment}\"',\n])\n\nterraform_query('plan')  # Export final HCL\n```\n\n## Capability Matrix\n\n| Capability | Status | Notes |\n|------------|--------|-------|\n| Resources, data sources, modules | Supported | All Terraform block types |\n| Variables with types and defaults | Supported | string, number, bool, list, map, object |\n| Outputs | Supported | Simple value expressions |\n| Nested blocks (ingress, egress, route, filter) | Supported | Via `nest` verb |\n| Tags | Supported | Via `style` verb with `tags:\"K=V,K2=V2\"` |\n| Rename blocks | Partial | `label` renames but does not cascade references |\n| Selectors for bulk operations | Partial | `remove @selector` works; `style @selector` not yet supported |\n| `jsonencode()` / HCL functions | Not supported | Complex expressions containing colons conflict with the DSL tokenizer |\n| Nested block removal/editing | Not supported | Nested blocks are append-only |\n| `count` / `for_each` meta-arguments | Not supported | `set` cannot modify meta-arguments |\n\n## Known Limitations\n\nThe following issues were identified through competitive validation against raw HCL writing. They are tracked for resolution.\n\n**HCL Serialization**\n\n- **List values in nested blocks render without quotes.** Attributes like `cidr_blocks`, `security_groups`, and `values` in filter/ingress/egress blocks output `[0.0.0.0/0]` instead of `[\"0.0.0.0/0\"]`. This produces invalid HCL. Workaround: post-process the output or use `terraform_query('plan')` output as a starting point for manual fixes.\n\n- **String values may lose their type.** Setting `engine_version:\"15\"` renders as `engine_version = 15` (number). The serializer does not preserve explicit string quoting for numeric-looking values.\n\n- **Output values with interpolation render unquoted.** An output `value:\"VPC: ${aws_vpc.main.id}\"` renders without surrounding quotes.\n\n**Labels**\n\n- **Labels are globally unique.** You cannot use `main` for both `aws_vpc.main` and `aws_internet_gateway.main` even though Terraform allows this (labels are scoped per type). Use distinct labels like `igw`, `rds`, `dbsubnet`.\n\n- **`label` rename breaks the lookup index.** After renaming a block with `label old new`, subsequent `style new` or `set new` calls may return \"block not found\". The internal index is not rebuilt after rename.\n\n**Nested Blocks**\n\n- **Cannot remove or edit specific nested blocks.** Once added via `nest`, a nested block (ingress rule, route, filter) cannot be individually removed or modified. If a `nest` call produces an error but partially succeeds, the ghost block persists.\n\n**Expressions**\n\n- **JSON with colons conflicts with the DSL tokenizer.** IAM `assume_role_policy` or similar JSON-containing attributes are parsed as key:value pairs by the FCP tokenizer, producing bare JSON objects instead of quoted strings or `jsonencode()` calls.\n\n## Development\n\n```bash\nnpm install\nnpm run build     # tsc\nnpm test          # vitest, 138 tests\n```\n\n## License\n\nMIT\n\n","readmeFilename":"README.md"}