diff --git a/openspec/changes/archive/2026-05-22-enforce-single-tool-type-with-port-config/.openspec.yaml b/openspec/changes/archive/2026-05-22-enforce-single-tool-type-with-port-config/.openspec.yaml
new file mode 100644
index 0000000..4a1c677
--- /dev/null
+++ b/openspec/changes/archive/2026-05-22-enforce-single-tool-type-with-port-config/.openspec.yaml
@@ -0,0 +1,2 @@
+schema: spec-driven
+created: 2026-05-22
diff --git a/openspec/changes/archive/2026-05-22-enforce-single-tool-type-with-port-config/design.md b/openspec/changes/archive/2026-05-22-enforce-single-tool-type-with-port-config/design.md
new file mode 100644
index 0000000..4d11597
--- /dev/null
+++ b/openspec/changes/archive/2026-05-22-enforce-single-tool-type-with-port-config/design.md
@@ -0,0 +1,69 @@
+## Context
+
+Currently, tool types use a JSON `interfaces` array (e.g., `["web"]`, `["terminal"]`, `["web", "terminal"]`) to define what interfaces a tool supports. This was designed for flexibility but in practice:
+1. No tool needs both web and terminal simultaneously
+2. Terminal tools don't expose ports or need tunneling
+3. The UI shows checkboxes for both, allowing invalid multi-select combinations
+
+The database migration `0008_tool_type_category` added the `interfaces` JSON column. All existing records use `["web"]` or `["terminal"]` as the first (and only) element.
+
+## Goals / Non-Goals
+
+**Goals:**
+- Replace `interfaces` array with single `interface_type` string column
+- Add `requires_port` boolean to indicate if port/tunnel config is relevant
+- Update UI to use dropdown instead of checkboxes
+- Conditionally hide port fields for terminal tools
+- Migrate existing data safely
+
+**Non-Goals:**
+- No changes to tool instance runtime behavior
+- No changes to tunnel/port infrastructure
+- No changes to existing tool configs (port_override remains in schema)
+
+## Decisions
+
+**Decision: Replace interfaces array with single interface_type string**
+- Rationale: Simplifies model, API, and UI. No legitimate use case for multiple interfaces.
+- Alternative: Keep array but enforce single item — rejected because it keeps unnecessary complexity
+
+**Decision: Add requires_port boolean instead of inferring from interface_type**
+- Rationale: Explicit is better than implicit. Future interface types may have different port needs.
+- Alternative: Infer from interface_type === "web" — rejected for flexibility
+
+**Decision: Default requires_port = true for existing records, then update per actual type**
+- Rationale: Most existing tools are web-based. Safer default.
+- Migration will inspect existing interfaces[0] to set correct value.
+
+**Decision: Keep port_override in tool_configs schema**
+- Rationale: Even terminal tools might need port overrides in edge cases. The UI just hides it.
+- Alternative: Remove column — rejected to avoid destructive migration
+
+## Risks / Trade-offs
+
+- **[Risk]** Existing API consumers expect `interfaces` array
+ - **Mitigation:** This is a **BREAKING** change. Update frontend simultaneously. Document in changelog.
+- **[Risk]** Data migration fails for unexpected interfaces values
+ - **Mitigation:** Migration takes first array element. Add fallback to "web" with requires_port=true.
+- **[Risk]** Tests break across backend and frontend
+ - **Mitigation:** Update all test fixtures and assertions in single commit.
+
+## Migration Plan
+
+1. Create Alembic migration to:
+ - Add `interface_type` string column (nullable temporarily)
+ - Add `requires_port` boolean column (default true)
+ - Migrate data: `interface_type = interfaces[0]`, `requires_port = (interfaces[0] == "web")`
+ - Drop `interfaces` column
+ - Make `interface_type` non-nullable
+2. Update Pydantic schemas (Create/Update/Response)
+3. Update SQLAlchemy model
+4. Update frontend types and API client
+5. Update tool workshop form (dropdown + conditional fields)
+6. Update built-in seed data
+7. Update tests
+8. Run full test suite
+
+## Open Questions
+
+None
\ No newline at end of file
diff --git a/openspec/changes/archive/2026-05-22-enforce-single-tool-type-with-port-config/proposal.md b/openspec/changes/archive/2026-05-22-enforce-single-tool-type-with-port-config/proposal.md
new file mode 100644
index 0000000..9b1b489
--- /dev/null
+++ b/openspec/changes/archive/2026-05-22-enforce-single-tool-type-with-port-config/proposal.md
@@ -0,0 +1,31 @@
+## Why
+
+Currently, tool types support multiple interfaces (e.g., `web` and `terminal` simultaneously), but in practice each tool serves a single purpose and should have one clear interface type. Additionally, terminal tools don't need ports or tunneling capabilities, yet the UI always shows port configuration. This creates confusion and allows invalid configurations.
+
+## What Changes
+
+- **BREAKING**: Change `interfaces` from an array (`["web"]`) to a single string (`"web"` or `"terminal"`) in the ToolType model, API, and frontend
+- Add `requires_port` boolean field to ToolType model — `true` for web tools, `false` for terminal tools
+- Update frontend UI to use a dropdown for interface type selection (single choice)
+- Conditionally show/hide port-related fields based on `requires_port`
+- Add database migration to convert existing `interfaces` arrays to single values and set `requires_port`
+- Update built-in tool types (code-server, jupyter-notebook) to use new schema
+- Update tool workshop page to reflect the single-type dropdown and conditional port visibility
+
+## Capabilities
+
+### New Capabilities
+- `tool-type-single-interface`: Enforce single interface type per tool with dropdown selection
+- `tool-type-port-visibility`: Conditionally show port/tunnel config based on tool interface type
+
+### Modified Capabilities
+- `tool-types-definition`: Update model and API to replace `interfaces` array with single `interface_type` string and add `requires_port`
+- `frontend-foundation`: Update tool workshop UI for single interface dropdown and conditional port fields
+
+## Impact
+
+- Database: Migration to change `interfaces` JSON column to `interface_type` string + add `requires_port` boolean
+- Backend API: Update Pydantic schemas, SQLAlchemy model, validation logic
+- Frontend: Update TypeScript types, tool workshop form, API client
+- Existing tool configs: No direct impact, but port_override field becomes irrelevant for terminal tools
+- Tests: Update test data and assertions for new schema
diff --git a/openspec/changes/archive/2026-05-22-enforce-single-tool-type-with-port-config/specs/frontend-foundation/spec.md b/openspec/changes/archive/2026-05-22-enforce-single-tool-type-with-port-config/specs/frontend-foundation/spec.md
new file mode 100644
index 0000000..5f5f89b
--- /dev/null
+++ b/openspec/changes/archive/2026-05-22-enforce-single-tool-type-with-port-config/specs/frontend-foundation/spec.md
@@ -0,0 +1,32 @@
+## ADDED Requirements
+
+### Requirement: Tool Interface Type Dropdown
+The tool workshop SHALL provide a dropdown for selecting a single interface type.
+
+#### Scenario: Interface type dropdown
+- GIVEN the tool workshop page
+- WHEN a user creates or edits a tool type
+- THEN the interface type field is a dropdown (not checkboxes)
+- AND the options are "web" and "terminal"
+- AND only one option can be selected
+
+### Requirement: Conditional Port Fields
+The tool workshop SHALL conditionally show or hide port-related fields based on the selected interface type.
+
+#### Scenario: Web tool shows port fields
+- GIVEN a tool type with interface type "web"
+- WHEN the user views the tool editor
+- THEN the Default Port field is visible and required
+- AND port-related config fields are shown
+
+#### Scenario: Terminal tool hides port fields
+- GIVEN a tool type with interface type "terminal"
+- WHEN the user views the tool editor
+- THEN the Default Port field is hidden
+- AND port-related config fields are hidden or disabled
+
+#### Scenario: Changing interface type updates visibility
+- GIVEN a user changes interface type from "web" to "terminal"
+- WHEN the change is applied
+- THEN port fields are immediately hidden
+- AND any port value is preserved but not validated
\ No newline at end of file
diff --git a/openspec/changes/archive/2026-05-22-enforce-single-tool-type-with-port-config/specs/tool-type-port-visibility/spec.md b/openspec/changes/archive/2026-05-22-enforce-single-tool-type-with-port-config/specs/tool-type-port-visibility/spec.md
new file mode 100644
index 0000000..a2e1acd
--- /dev/null
+++ b/openspec/changes/archive/2026-05-22-enforce-single-tool-type-with-port-config/specs/tool-type-port-visibility/spec.md
@@ -0,0 +1,50 @@
+## ADDED Requirements
+
+### Requirement: Port Configuration Visibility
+The system SHALL control whether port configuration is relevant for a tool type.
+
+#### Scenario: Web tool requires port
+- GIVEN a tool type with `requires_port` = true
+- WHEN the tool type is displayed in the UI
+- THEN port configuration fields are shown
+- AND default_port is validated as required
+
+#### Scenario: Terminal tool does not require port
+- GIVEN a tool type with `requires_port` = false
+- WHEN the tool type is displayed in the UI
+- THEN port configuration fields are hidden
+- AND default_port validation is skipped
+- AND port_override in tool configs is not shown
+
+### Requirement: Port Validation Based on requires_port
+The API SHALL validate port fields conditionally based on requires_port.
+
+#### Scenario: Validate port for web tools
+- GIVEN a tool type with `requires_port` = true
+- WHEN creating or updating without a default_port
+- THEN the system returns 400 Bad Request
+
+#### Scenario: Skip port validation for terminal tools
+- GIVEN a tool type with `requires_port` = false
+- WHEN creating or updating without a default_port
+- THEN the request succeeds
+- AND default_port defaults to 0 or null
+
+### Requirement: UI Conditional Rendering
+The frontend SHALL conditionally render port-related UI elements.
+
+#### Scenario: Hide port in tool list
+- GIVEN a terminal tool type
+- WHEN displayed in the tool workshop list
+- THEN port information is not shown
+
+#### Scenario: Hide port in editor
+- GIVEN a terminal tool type being edited
+- WHEN the editor form is rendered
+- THEN the Default Port field is hidden
+- AND the readiness probe fields are shown (still relevant)
+
+#### Scenario: Show port for web tools
+- GIVEN a web tool type being edited
+- WHEN the editor form is rendered
+- THEN the Default Port field is visible and required
\ No newline at end of file
diff --git a/openspec/changes/archive/2026-05-22-enforce-single-tool-type-with-port-config/specs/tool-type-single-interface/spec.md b/openspec/changes/archive/2026-05-22-enforce-single-tool-type-with-port-config/specs/tool-type-single-interface/spec.md
new file mode 100644
index 0000000..2f28997
--- /dev/null
+++ b/openspec/changes/archive/2026-05-22-enforce-single-tool-type-with-port-config/specs/tool-type-single-interface/spec.md
@@ -0,0 +1,39 @@
+## ADDED Requirements
+
+### Requirement: Single Interface Type Enforcement
+The system SHALL enforce that each tool type has exactly one interface type.
+
+#### Scenario: Create with single interface
+- GIVEN a tool type creation request with `interface_type` = "web"
+- WHEN the request is processed
+- THEN the tool type is created successfully
+- AND the interface type is stored as a single string
+
+#### Scenario: Reject multiple interfaces
+- GIVEN a legacy request with `interfaces` array
+- WHEN the request is processed
+- THEN the system returns 400 Bad Request
+- AND the error message indicates that `interface_type` (string) should be used instead
+
+### Requirement: Interface Type Validation
+The system SHALL validate that interface_type is one of the allowed values.
+
+#### Scenario: Valid interface types
+- GIVEN interface_type values "web" or "terminal"
+- WHEN a tool type is created or updated
+- THEN the request is accepted
+
+#### Scenario: Invalid interface type
+- GIVEN interface_type value "ssh"
+- WHEN a tool type is created or updated
+- THEN the system returns 400 Bad Request
+
+### Requirement: Data Migration
+The system SHALL migrate existing tool types from interfaces array to single interface_type.
+
+#### Scenario: Migrate existing records
+- GIVEN existing tool types with interfaces = ["web"] or ["terminal"]
+- WHEN the migration runs
+- THEN each record gets interface_type = interfaces[0]
+- AND requires_port is set based on the interface type
+- AND the old interfaces column is removed
\ No newline at end of file
diff --git a/openspec/changes/archive/2026-05-22-enforce-single-tool-type-with-port-config/specs/tool-types-definition/spec.md b/openspec/changes/archive/2026-05-22-enforce-single-tool-type-with-port-config/specs/tool-types-definition/spec.md
new file mode 100644
index 0000000..b187e94
--- /dev/null
+++ b/openspec/changes/archive/2026-05-22-enforce-single-tool-type-with-port-config/specs/tool-types-definition/spec.md
@@ -0,0 +1,80 @@
+## MODIFIED Requirements
+
+### Requirement: Tool Type Model
+The system SHALL provide a `ToolType` model to store tool definitions.
+
+#### Scenario: Model structure
+- GIVEN a tool type definition
+- THEN the model SHALL have:
+ - `id`: UUID primary key
+ - `name`: unique string (e.g., "code-server")
+ - `display_name`: human-readable string (e.g., "VS Code Server")
+ - `description`: optional text
+ - `category`: string (e.g., "editor", "notebook")
+ - `interface_type`: single string — "web" or "terminal"
+ - `requires_port`: boolean indicating if port/tunnel configuration is needed
+ - `compose_template`: Docker Compose YAML string
+ - `dockerfile_template`: Dockerfile string
+ - `definition_type`: string — "compose" or "dockerfile"
+ - `required_variables`: list of required template variables
+ - `is_builtin`: boolean flag for system-defined types
+ - `created_at`/`updated_at`: timestamps
+
+### Requirement: CRUD API Endpoints
+The system SHALL provide REST API endpoints for tool type management.
+
+#### Scenario: Create tool type
+- GIVEN an admin user
+- WHEN they POST /api/tool-types with valid data
+- THEN the system creates a new tool type
+- AND validates `interface_type` is "web" or "terminal"
+- AND validates `requires_port` is boolean
+- AND validates the compose template YAML (if definition_type is "compose")
+- AND validates all required variables are present in template
+- AND returns 201 Created with the new tool type
+
+#### Scenario: Update tool type
+- GIVEN an admin user
+- WHEN they PUT /api/tool-types/{id} with valid data
+- THEN the system updates the tool type
+- AND validates `interface_type` is "web" or "terminal" if provided
+- AND re-validates the compose template
+- AND returns 200 OK with updated tool type
+
+## REMOVED Requirements
+
+### Requirement: Multiple interfaces support
+**Reason**: Tool types now use a single `interface_type` instead of an array of interfaces. No tool legitimately needs both web and terminal interfaces simultaneously.
+**Migration**: Use `interface_type` field (string) instead of `interfaces` array. Set to "web" or "terminal".
+
+## ADDED Requirements
+
+### Requirement: Port requirement indication
+The system SHALL allow tool types to indicate whether they require port configuration.
+
+#### Scenario: Web tool requires port
+- GIVEN a tool type with `interface_type` = "web"
+- WHEN the tool type is created or updated
+- THEN `requires_port` SHALL default to true
+- AND port-related configuration is shown in the UI
+
+#### Scenario: Terminal tool does not require port
+- GIVEN a tool type with `interface_type` = "terminal"
+- WHEN the tool type is created or updated
+- THEN `requires_port` SHALL default to false
+- AND port-related configuration is hidden in the UI
+
+### Requirement: Single interface validation
+The system SHALL enforce that each tool type has exactly one interface type.
+
+#### Scenario: Invalid interface type
+- GIVEN a tool type creation request with `interface_type` = "invalid"
+- WHEN the request is processed
+- THEN the system returns 400 Bad Request
+- AND the error message indicates valid values are "web" or "terminal"
+
+#### Scenario: Missing interface type
+- GIVEN a tool type creation request without `interface_type`
+- WHEN the request is processed
+- THEN the system returns 400 Bad Request
+- AND the error message indicates interface_type is required
\ No newline at end of file
diff --git a/openspec/changes/archive/2026-05-22-enforce-single-tool-type-with-port-config/tasks.md b/openspec/changes/archive/2026-05-22-enforce-single-tool-type-with-port-config/tasks.md
new file mode 100644
index 0000000..ef9119a
--- /dev/null
+++ b/openspec/changes/archive/2026-05-22-enforce-single-tool-type-with-port-config/tasks.md
@@ -0,0 +1,41 @@
+## 1. Database Migration
+
+- [x] 1.1 Create Alembic migration to add `interface_type` string column and `requires_port` boolean column to `tool_types` table
+- [x] 1.2 Write migration logic to populate `interface_type` from `interfaces[0]` and set `requires_port` based on value
+- [x] 1.3 Drop `interfaces` JSON column and make `interface_type` non-nullable
+
+## 2. Backend Model & API Updates
+
+- [x] 2.1 Update SQLAlchemy model (`apps/api/src/models/tool_type.py`) — replace `interfaces` list with `interface_type` string and add `requires_port` boolean
+- [x] 2.2 Update Pydantic schemas (`apps/api/src/api/tool_types.py`) — `ToolTypeCreate`, `ToolTypeUpdate`, `ToolTypeResponse`
+- [x] 2.3 Add validation for `interface_type` (must be "web" or "terminal")
+- [x] 2.4 Update default values and built-in tool type seeding logic
+- [x] 2.5 Update API tests for new schema
+
+## 3. Frontend Type & API Updates
+
+- [x] 3.1 Update TypeScript interfaces (`apps/web/src/api/tool_types.ts`) — replace `interfaces: string[]` with `interface_type: string` and add `requires_port: boolean`
+- [x] 3.2 Update API request/response types (`CreateToolTypeRequest`, `UpdateToolTypeRequest`)
+
+## 4. Tool Workshop UI Updates
+
+- [x] 4.1 Replace interface checkboxes with dropdown (single-select) in tool editor
+- [x] 4.2 Add conditional rendering for port field based on `requires_port` / `interface_type`
+- [x] 4.3 Update tool list to show `interface_type` instead of interfaces array
+- [x] 4.4 Update form state management for new fields
+- [x] 4.5 Update dirty state tracking
+
+## 5. Test Updates
+
+- [x] 5.1 Update backend API tests (`test_tool_types_api.py`) for new schema
+- [x] 5.2 Update frontend tests (`tool-workshop.test.tsx`) for dropdown and conditional fields
+- [x] 5.3 Update mock data fixtures
+
+## 6. Verification & Cleanup
+
+- [x] 6.1 Run backend tests: `pytest apps/api/tests/`
+- [x] 6.2 Run frontend typecheck: `npm run typecheck`
+- [x] 6.3 Run frontend tests: `npm run test`
+- [x] 6.4 Run lint: `npm run lint`
+- [x] 6.5 Verify migration applies cleanly to existing database
+- [x] 6.6 Update documentation if needed
\ No newline at end of file
diff --git a/openspec/specs/frontend-foundation/spec.md b/openspec/specs/frontend-foundation/spec.md
index 9c0f9fa..3839f0a 100644
--- a/openspec/specs/frontend-foundation/spec.md
+++ b/openspec/specs/frontend-foundation/spec.md
@@ -111,6 +111,37 @@ The system SHALL provide a dashboard overview.
- Recent activity
- Quick action buttons
+### Requirement: Tool Interface Type Dropdown
+The tool workshop SHALL provide a dropdown for selecting a single interface type.
+
+#### Scenario: Interface type dropdown
+- GIVEN the tool workshop page
+- WHEN a user creates or edits a tool type
+- THEN the interface type field is a dropdown (not checkboxes)
+- AND the options are "web" and "terminal"
+- AND only one option can be selected
+
+### Requirement: Conditional Port Fields
+The tool workshop SHALL conditionally show or hide port-related fields based on the selected interface type.
+
+#### Scenario: Web tool shows port fields
+- GIVEN a tool type with interface type "web"
+- WHEN the user views the tool editor
+- THEN the Default Port field is visible and required
+- AND port-related config fields are shown
+
+#### Scenario: Terminal tool hides port fields
+- GIVEN a tool type with interface type "terminal"
+- WHEN the user views the tool editor
+- THEN the Default Port field is hidden
+- AND port-related config fields are hidden or disabled
+
+#### Scenario: Changing interface type updates visibility
+- GIVEN a user changes interface type from "web" to "terminal"
+- WHEN the change is applied
+- THEN port fields are immediately hidden
+- AND any port value is preserved but not validated
+
## Dependencies
- React 18+
diff --git a/openspec/specs/tool-type-port-visibility/spec.md b/openspec/specs/tool-type-port-visibility/spec.md
new file mode 100644
index 0000000..3463e56
--- /dev/null
+++ b/openspec/specs/tool-type-port-visibility/spec.md
@@ -0,0 +1,50 @@
+## ADDED Requirements
+
+### Requirement: Port Configuration Visibility
+The system SHALL control whether port configuration is relevant for a tool type.
+
+#### Scenario: Web tool requires port
+- GIVEN a tool type with `requires_port` = true
+- WHEN the tool type is displayed in the UI
+- THEN port configuration fields are shown
+- AND default_port is validated as required
+
+#### Scenario: Terminal tool does not require port
+- GIVEN a tool type with `requires_port` = false
+- WHEN the tool type is displayed in the UI
+- THEN port configuration fields are hidden
+- AND default_port validation is skipped
+- AND port_override in tool configs is not shown
+
+### Requirement: Port Validation Based on requires_port
+The API SHALL validate port fields conditionally based on requires_port.
+
+#### Scenario: Validate port for web tools
+- GIVEN a tool type with `requires_port` = true
+- WHEN creating or updating without a default_port
+- THEN the system returns 400 Bad Request
+
+#### Scenario: Skip port validation for terminal tools
+- GIVEN a tool type with `requires_port` = false
+- WHEN creating or updating without a default_port
+- THEN the request succeeds
+- AND default_port defaults to 0 or null
+
+### Requirement: UI Conditional Rendering
+The frontend SHALL conditionally render port-related UI elements.
+
+#### Scenario: Hide port in tool list
+- GIVEN a terminal tool type
+- WHEN displayed in the tool workshop list
+- THEN port information is not shown
+
+#### Scenario: Hide port in editor
+- GIVEN a terminal tool type being edited
+- WHEN the editor form is rendered
+- THEN the Default Port field is hidden
+- AND the readiness probe fields are shown (still relevant)
+
+#### Scenario: Show port for web tools
+- GIVEN a web tool type being edited
+- WHEN the editor form is rendered
+- THEN the Default Port field is visible and required
diff --git a/openspec/specs/tool-type-single-interface/spec.md b/openspec/specs/tool-type-single-interface/spec.md
new file mode 100644
index 0000000..15c460a
--- /dev/null
+++ b/openspec/specs/tool-type-single-interface/spec.md
@@ -0,0 +1,39 @@
+## ADDED Requirements
+
+### Requirement: Single Interface Type Enforcement
+The system SHALL enforce that each tool type has exactly one interface type.
+
+#### Scenario: Create with single interface
+- GIVEN a tool type creation request with `interface_type` = "web"
+- WHEN the request is processed
+- THEN the tool type is created successfully
+- AND the interface type is stored as a single string
+
+#### Scenario: Reject multiple interfaces
+- GIVEN a legacy request with `interfaces` array
+- WHEN the request is processed
+- THEN the system returns 400 Bad Request
+- AND the error message indicates that `interface_type` (string) should be used instead
+
+### Requirement: Interface Type Validation
+The system SHALL validate that interface_type is one of the allowed values.
+
+#### Scenario: Valid interface types
+- GIVEN interface_type values "web" or "terminal"
+- WHEN a tool type is created or updated
+- THEN the request is accepted
+
+#### Scenario: Invalid interface type
+- GIVEN interface_type value "ssh"
+- WHEN a tool type is created or updated
+- THEN the system returns 400 Bad Request
+
+### Requirement: Data Migration
+The system SHALL migrate existing tool types from interfaces array to single interface_type.
+
+#### Scenario: Migrate existing records
+- GIVEN existing tool types with interfaces = ["web"] or ["terminal"]
+- WHEN the migration runs
+- THEN each record gets interface_type = interfaces[0]
+- AND requires_port is set based on the interface type
+- AND the old interfaces column is removed
diff --git a/openspec/specs/tool-types-definition/spec.md b/openspec/specs/tool-types-definition/spec.md
index 394ab5d..da05e01 100644
--- a/openspec/specs/tool-types-definition/spec.md
+++ b/openspec/specs/tool-types-definition/spec.md
@@ -11,7 +11,12 @@ The system SHALL provide a `ToolType` model to store tool definitions.
- `name`: unique string (e.g., "code-server")
- `display_name`: human-readable string (e.g., "VS Code Server")
- `description`: optional text
+ - `category`: string (e.g., "editor", "notebook")
+ - `interface_type`: single string — "web" or "terminal"
+ - `requires_port`: boolean indicating if port/tunnel configuration is needed
- `compose_template`: Docker Compose YAML string
+ - `dockerfile_template`: Dockerfile string
+ - `definition_type`: string — "compose" or "dockerfile"
- `required_variables`: list of required template variables
- `is_builtin`: boolean flag for system-defined types
- `created_at`/`updated_at`: timestamps
@@ -30,7 +35,9 @@ The system SHALL provide REST API endpoints for tool type management.
- GIVEN an admin user
- WHEN they POST /api/tool-types with valid data
- THEN the system creates a new tool type
-- AND validates the compose template YAML
+- AND validates `interface_type` is "web" or "terminal"
+- AND validates `requires_port` is boolean
+- AND validates the compose template YAML (if definition_type is "compose")
- AND validates all required variables are present in template
- AND returns 201 Created with the new tool type
@@ -44,6 +51,7 @@ The system SHALL provide REST API endpoints for tool type management.
- GIVEN an admin user
- WHEN they PUT /api/tool-types/{id} with valid data
- THEN the system updates the tool type
+- AND validates `interface_type` is "web" or "terminal" if provided
- AND re-validates the compose template
- AND returns 200 OK with updated tool type
@@ -98,3 +106,33 @@ The system SHALL validate Docker Compose templates.
- THEN the system SHALL require:
- `services` key present
- At least one service defined
+
+### Requirement: Port requirement indication
+The system SHALL allow tool types to indicate whether they require port configuration.
+
+#### Scenario: Web tool requires port
+- GIVEN a tool type with `interface_type` = "web"
+- WHEN the tool type is created or updated
+- THEN `requires_port` SHALL default to true
+- AND port-related configuration is shown in the UI
+
+#### Scenario: Terminal tool does not require port
+- GIVEN a tool type with `interface_type` = "terminal"
+- WHEN the tool type is created or updated
+- THEN `requires_port` SHALL default to false
+- AND port-related configuration is hidden in the UI
+
+### Requirement: Single interface validation
+The system SHALL enforce that each tool type has exactly one interface type.
+
+#### Scenario: Invalid interface type
+- GIVEN a tool type creation request with `interface_type` = "invalid"
+- WHEN the request is processed
+- THEN the system returns 400 Bad Request
+- AND the error message indicates valid values are "web" or "terminal"
+
+#### Scenario: Missing interface type
+- GIVEN a tool type creation request without `interface_type`
+- WHEN the request is processed
+- THEN the system returns 400 Bad Request
+- AND the error message indicates interface_type is required