diff --git a/openspec/changes/update-mcp-server-for-organization-members/design.md b/openspec/changes/update-mcp-server-for-organization-members/design.md new file mode 100644 index 0000000..902602a --- /dev/null +++ b/openspec/changes/update-mcp-server-for-organization-members/design.md @@ -0,0 +1,154 @@ +# Design: Update MCP Server for Organization Members + +## MCP Tool Definition Changes + +### Tool: create_member +**Current State:** +- Description: "Create a new member. Only firstName is required..." +- Required field: `firstName` +- Missing fields: `memberType`, `companyName` + +**Changes Needed:** +- Update description to explain conditional requirements: + - INDIVIDUAL members require `firstName` + - ORGANIZATION members require `companyName` + - `memberType` defaults to INDIVIDUAL if not specified +- Add `memberType` property (enum: INDIVIDUAL, ORGANIZATION) +- Add `companyName` property (string, optional) +- Make `firstName` optional in schema (validation happens server-side) +- Remove `firstName` from required array + +### Tool: update_member +**Current State:** +- Missing fields: `memberType`, `companyName` + +**Changes Needed:** +- Add `memberType` property (enum: INDIVIDUAL, ORGANIZATION) +- Add `companyName` property (string, optional) +- Update description to mention type transitions + +### Tools: list_members and get_member +**Current State:** +- No input changes needed (query tools) +- Output fields come from GraphQL queries + +**Changes Needed:** +- No changes to tool definitions +- GraphQL queries will be updated to include new fields + +## GraphQL Query Updates + +### Query: listMembers (line ~297) +**Add fields:** +```graphql +memberType +companyName +``` + +### Query: getMember (line ~328) +**Add fields:** +```graphql +memberType +companyName +``` + +### Mutation: createMember (line ~371) +**Add fields to response:** +```graphql +memberType +companyName +``` + +### Mutation: updateMember (line ~402) +**Add fields to response:** +```graphql +memberType +companyName +``` + +## Member Formatting Logic + +### Function: formatMember (line ~580) +**Current State:** +- Line 583: Always displays "Name: firstName lastName" +- Assumes all members are individuals + +**Changes Needed:** +1. Check `member.memberType` to determine formatting approach +2. For INDIVIDUAL members: + - Display: `Type: Individual` + - Display: `Name: firstName lastName` +3. For ORGANIZATION members: + - Display: `Type: Organization` + - Display: `Company: companyName` + - Optionally display: `Contact Person: firstName lastName` (if present) + +**Example Output:** + +For Individual: +``` +ID: 1 +Type: Individual +Name: John Doe +Email: john@example.com +... +``` + +For Organization: +``` +ID: 2 +Type: Organization +Company: Acme Corporation +Contact Person: Jane Smith +Email: info@acme.com +... +``` + +### Function: formatMembers (line ~622) +**Current State:** +- Calls `formatMember()` for each member +- No changes needed (delegates to formatMember) + +## Schema Discovery + +The MCP server includes a `get_graphql_schema` tool that performs introspection. This tool will automatically discover the new `MemberType` enum without code changes, but we should verify it works correctly. + +## Implementation Strategy + +1. **Update tool definitions** (create_member and update_member) + - Add memberType and companyName properties + - Update descriptions + - Adjust required fields + +2. **Update GraphQL queries/mutations** + - Add memberType and companyName to all member queries + - Add to all mutation responses + +3. **Update formatMember function** + - Add type checking logic + - Branch formatting based on memberType + - Handle both individual and organization display + +4. **Test changes** + - Verify individual member creation (existing behavior) + - Verify organization member creation (new behavior) + - Verify member listing shows correct formatting + - Verify schema introspection includes MemberType enum + +## Backward Compatibility + +- Existing MCP clients not providing `memberType` will default to INDIVIDUAL (GraphQL default) +- Existing queries will work as before (new fields are optional in responses) +- Member formatting will gracefully handle missing memberType (defaults to INDIVIDUAL) + +## File Changes Summary + +**Single File to Modify:** +- `src/mcp_server.ts` - All changes are in this file + - Lines 154-189: update_member tool definition + - Lines 191-228: update_member tool definition + - Lines 297-314: listMembers query + - Lines 328-344: getMember query + - Lines 371-388: createMember mutation + - Lines 402-419: updateMember mutation + - Lines 580-620: formatMember function diff --git a/openspec/changes/update-mcp-server-for-organization-members/proposal.md b/openspec/changes/update-mcp-server-for-organization-members/proposal.md new file mode 100644 index 0000000..3eef307 --- /dev/null +++ b/openspec/changes/update-mcp-server-for-organization-members/proposal.md @@ -0,0 +1,30 @@ +# Proposal: Update MCP Server for Organization Members + +## Summary +Update the TypeScript MCP server to support the new organization member functionality that was implemented in the GraphQL backend. The MCP server currently only exposes tools for individual members and needs to be updated to handle both INDIVIDUAL and ORGANIZATION member types. + +## Background +The GraphQL API was recently updated to support both individual and organization members through the `support-organization-members` change. This added: +- `memberType` field (enum: INDIVIDUAL, ORGANIZATION) +- `companyName` field for organizations +- Made `firstName` nullable (required for individuals, optional for organizations) +- Conditional validation based on member type + +However, the TypeScript MCP server (`src/mcp_server.ts`) still assumes all members are individuals with a required `firstName` field. The MCP tools don't expose the new fields, and the formatting logic doesn't distinguish between member types. + +## Goals +- Update MCP tool definitions to include `memberType` and `companyName` fields +- Update tool descriptions to explain conditional requirements (INDIVIDUAL requires firstName, ORGANIZATION requires companyName) +- Update all GraphQL queries/mutations in the MCP server to include new fields +- Update member formatting logic to distinguish between individuals and organizations +- Maintain backward compatibility (memberType defaults to INDIVIDUAL) + +## Non-Goals +- Changing the GraphQL schema (already complete) +- Adding new MCP tools (only updating existing tools) +- Modifying validation logic (handled by GraphQL backend) + +## Technical Note +- The MCP server is written in TypeScript and uses the Model Context Protocol SDK +- Field names use camelCase in MCP tools (matching GraphQL conventions) +- The GraphQL backend already validates conditional requirements server-side diff --git a/openspec/changes/update-mcp-server-for-organization-members/specs/mcp-member-tools/spec.md b/openspec/changes/update-mcp-server-for-organization-members/specs/mcp-member-tools/spec.md new file mode 100644 index 0000000..a3bc562 --- /dev/null +++ b/openspec/changes/update-mcp-server-for-organization-members/specs/mcp-member-tools/spec.md @@ -0,0 +1,159 @@ +# Spec Delta: MCP Member Tools for Organization Support + +## MODIFIED Requirements + +### Requirement: Create Member Tool +The MCP server SHALL provide a `create_member` tool that creates both individual and organization members via the GraphQL API. + +#### Scenario: Create individual member with first name +- **GIVEN** memberType is "INDIVIDUAL" or not specified +- **WHEN** the `create_member` tool is invoked with firstName="Alice" +- **THEN** the tool SHALL create a new individual member with firstName="Alice" +- **AND** return the created member with generated ID and memberType="INDIVIDUAL" + +#### Scenario: Create organization member with company name +- **GIVEN** memberType is "ORGANIZATION" +- **WHEN** the `create_member` tool is invoked with memberType="ORGANIZATION" and companyName="Acme Corp" +- **THEN** the tool SHALL create a new organization member with companyName="Acme Corp" +- **AND** return the created member with generated ID and memberType="ORGANIZATION" + +#### Scenario: Create organization with contact person +- **GIVEN** memberType is "ORGANIZATION" +- **WHEN** the `create_member` tool is invoked with memberType="ORGANIZATION", companyName="Tech Inc", and firstName="Jane" +- **THEN** the tool SHALL create an organization member with both company name and contact person +- **AND** return the created member with all fields populated + +#### Scenario: Create individual without firstName +- **GIVEN** memberType is "INDIVIDUAL" or not specified +- **WHEN** the `create_member` tool is invoked without firstName +- **THEN** the tool SHALL return a validation error indicating firstName is required for individual members + +#### Scenario: Create organization without companyName +- **GIVEN** memberType is "ORGANIZATION" +- **WHEN** the `create_member` tool is invoked without companyName +- **THEN** the tool SHALL return a validation error indicating companyName is required for organization members + +### Requirement: Update Member Tool +The MCP server SHALL provide an `update_member` tool that updates existing member information including member type transitions. + +#### Scenario: Update member type from individual to organization +- **GIVEN** a member exists with ID 1 as type INDIVIDUAL +- **WHEN** the `update_member` tool is invoked with id=1, memberType="ORGANIZATION", and companyName="New Corp" +- **THEN** the tool SHALL update the member to type ORGANIZATION +- **AND** return the updated member with memberType="ORGANIZATION" and companyName="New Corp" + +#### Scenario: Update organization company name +- **GIVEN** a member exists with ID 2 as type ORGANIZATION with companyName="Old Corp" +- **WHEN** the `update_member` tool is invoked with id=2 and companyName="Updated Corp" +- **THEN** the tool SHALL update the company name +- **AND** return the updated member with companyName="Updated Corp" + +#### Scenario: Update individual to organization without companyName +- **GIVEN** a member exists with ID 1 as type INDIVIDUAL +- **WHEN** the `update_member` tool is invoked with id=1 and memberType="ORGANIZATION" but no companyName +- **THEN** the tool SHALL return a validation error indicating companyName is required for organization members + +### Requirement: List Members Tool Output +The `list_members` tool SHALL return member data including memberType and companyName fields. + +#### Scenario: List members with mixed types +- **GIVEN** the database contains 2 individual members and 1 organization member +- **WHEN** the `list_members` tool is invoked +- **THEN** the tool SHALL return all 3 members +- **AND** each member SHALL include memberType field +- **AND** organization members SHALL include companyName field +- **AND** formatting SHALL distinguish between individuals and organizations + +### Requirement: Get Member Tool Output +The `get_member` tool SHALL return member data including memberType and companyName fields. + +#### Scenario: Get individual member by ID +- **GIVEN** a member exists with ID 1 as type INDIVIDUAL +- **WHEN** the `get_member` tool is invoked with id=1 +- **THEN** the tool SHALL return the member with memberType="INDIVIDUAL" +- **AND** format the output showing "Type: Individual" and "Name: firstName lastName" + +#### Scenario: Get organization member by ID +- **GIVEN** a member exists with ID 2 as type ORGANIZATION with companyName="Acme Corp" +- **WHEN** the `get_member` tool is invoked with id=2 +- **THEN** the tool SHALL return the member with memberType="ORGANIZATION" and companyName="Acme Corp" +- **AND** format the output showing "Type: Organization" and "Company: Acme Corp" + +### Requirement: Member Formatting +The MCP server SHALL format member output differently based on member type. + +#### Scenario: Format individual member +- **GIVEN** a member object with memberType="INDIVIDUAL", firstName="John", lastName="Doe" +- **WHEN** the formatMember function is called +- **THEN** the output SHALL include "Type: Individual" +- **AND** the output SHALL include "Name: John Doe" + +#### Scenario: Format organization member +- **GIVEN** a member object with memberType="ORGANIZATION", companyName="Tech Corp" +- **WHEN** the formatMember function is called +- **THEN** the output SHALL include "Type: Organization" +- **AND** the output SHALL include "Company: Tech Corp" + +#### Scenario: Format organization with contact person +- **GIVEN** a member object with memberType="ORGANIZATION", companyName="Tech Corp", firstName="Jane", lastName="Smith" +- **WHEN** the formatMember function is called +- **THEN** the output SHALL include "Type: Organization" +- **AND** the output SHALL include "Company: Tech Corp" +- **AND** the output SHALL include "Contact Person: Jane Smith" + +#### Scenario: Format member with missing memberType +- **GIVEN** a member object without memberType field (legacy data) +- **WHEN** the formatMember function is called +- **THEN** the function SHALL default to treating it as INDIVIDUAL +- **AND** format the output using individual member format + +## ADDED Requirements + +### Requirement: Tool Input Schema for Member Type +The `create_member` and `update_member` tools SHALL accept memberType and companyName in their input schemas. + +#### Scenario: Tool accepts memberType enum values +- **GIVEN** the create_member tool definition +- **WHEN** inspecting the input schema +- **THEN** memberType SHALL be defined as an enum with values "INDIVIDUAL" and "ORGANIZATION" +- **AND** memberType SHALL be optional with default value "INDIVIDUAL" + +#### Scenario: Tool accepts companyName field +- **GIVEN** the create_member tool definition +- **WHEN** inspecting the input schema +- **THEN** companyName SHALL be defined as a string type +- **AND** companyName SHALL be optional +- **AND** the description SHALL indicate it's required for ORGANIZATION members + +#### Scenario: firstName is optional in schema +- **GIVEN** the create_member tool definition +- **WHEN** inspecting the input schema +- **THEN** firstName SHALL NOT be in the required array +- **AND** the description SHALL indicate it's required for INDIVIDUAL members + +### Requirement: GraphQL Query Completeness +All GraphQL queries and mutations in the MCP server SHALL include memberType and companyName fields. + +#### Scenario: listMembers query includes new fields +- **GIVEN** the listMembers GraphQL query +- **WHEN** the query is executed +- **THEN** the query SHALL request memberType field +- **AND** the query SHALL request companyName field + +#### Scenario: getMember query includes new fields +- **GIVEN** the getMember GraphQL query +- **WHEN** the query is executed +- **THEN** the query SHALL request memberType field +- **AND** the query SHALL request companyName field + +#### Scenario: createMember mutation includes new fields in response +- **GIVEN** the createMember mutation +- **WHEN** the mutation is executed +- **THEN** the mutation SHALL request memberType in the response +- **AND** the mutation SHALL request companyName in the response + +#### Scenario: updateMember mutation includes new fields in response +- **GIVEN** the updateMember mutation +- **WHEN** the mutation is executed +- **THEN** the mutation SHALL request memberType in the response +- **AND** the mutation SHALL request companyName in the response diff --git a/openspec/changes/update-mcp-server-for-organization-members/tasks.md b/openspec/changes/update-mcp-server-for-organization-members/tasks.md new file mode 100644 index 0000000..b8b7e4c --- /dev/null +++ b/openspec/changes/update-mcp-server-for-organization-members/tasks.md @@ -0,0 +1,15 @@ +# Tasks: Update MCP Server for Organization Members + +- [ ] Update `create_member` tool definition with memberType and companyName fields +- [ ] Update `update_member` tool definition with memberType and companyName fields +- [ ] Add memberType and companyName to listMembers GraphQL query +- [ ] Add memberType and companyName to getMember GraphQL query +- [ ] Add memberType and companyName to createMember mutation response +- [ ] Add memberType and companyName to updateMember mutation response +- [ ] Update formatMember function to distinguish between individual and organization members +- [ ] Test individual member creation (verify existing behavior) +- [ ] Test organization member creation (verify new behavior) +- [ ] Test member listing with mixed member types (verify formatting) +- [ ] Verify GraphQL schema introspection includes MemberType enum + +**Note**: All changes are in a single file: `src/mcp_server.ts`