Archive completed change proposal to openspec/changes/archive/. Updated specs with new GraphQL introspection and MCP tool requirements: - graphql-api: Added GraphQL Introspection Support requirement - mcp-integration: Added 3 requirements for GraphQL tools - Get GraphQL Schema Tool - Execute GraphQL Query Tool - Tool Input Schema for GraphQL Execution All requirements include comprehensive scenarios and are now part of the active specification. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
9.8 KiB
graphql-api Specification
Purpose
TBD - created by archiving change add-graphql-member-api. Update Purpose after archive.
Requirements
Requirement: Strawberry GraphQL schema with Member type
The system MUST define a GraphQL schema using Strawberry with Member object type mapping to database model fields.
Member GraphQL type fields:
id: ID! (non-null unique identifier)firstName: String! (non-null)lastName: String (nullable)street: String (nullable)apartmentNumber: String (nullable)zip: String (nullable)city: String (nullable)country: String (nullable)email: String (nullable)phone: String (nullable)createdAt: DateTime! (non-null timestamp)updatedAt: DateTime! (non-null timestamp)
Scenario: Member type is defined with Strawberry decorator
Given src/schemas/member.py exists When Member class is decorated with @strawberry.type Then GraphQL type "Member" is registered in schema And all fields use camelCase naming (GraphQL convention) And field types match SQLAlchemy model types And snake_case database fields map to camelCase GraphQL fields
Scenario: GraphQL schema is introspectable
Given FastAPI app with GraphQL endpoint is running When client accesses /graphql endpoint Then GraphiQL playground is displayed And schema introspection reveals Member type And all fields and their types are documented
Requirement: Input types for mutations
The system MUST define separate input types for create and update operations with appropriate field requirements.
CreateMemberInput fields (only firstName required):
firstName: String!lastName: Stringstreet: StringapartmentNumber: Stringzip: Stringcity: Stringcountry: Stringemail: Stringphone: String
UpdateMemberInput fields (id required, all others optional):
id: ID!firstName: StringlastName: Stringstreet: StringapartmentNumber: Stringzip: Stringcity: Stringcountry: Stringemail: Stringphone: String
Scenario: CreateMemberInput enforces required fields
Given CreateMemberInput type is defined When client sends mutation without firstName Then GraphQL validation error is returned And error message indicates firstName is required When client sends mutation with only firstName Then input validation passes And member is created with only firstName populated
Scenario: UpdateMemberInput allows partial updates
Given UpdateMemberInput type is defined When client sends mutation with only id and email Then only email field is updated in database And all other fields remain unchanged When client sends mutation without id Then GraphQL validation error is returned
Requirement: Query resolvers for reading members
The system MUST provide GraphQL query resolvers for fetching individual members and listing all members.
Query operations:
member(id: ID!): Member- Get single member by IDmembers: [Member!]!- List all members
Scenario: Query single member by ID
Given member exists with id=1 When client executes query:
query {
member(id: 1) {
id
firstName
lastName
email
}
}
Then response contains member data And response matches GraphQL Member type structure
Scenario: Query member with non-existent ID
Given no member exists with id=999
When client executes query member(id: 999)
Then response returns null for member field
And no error is raised (null is valid for nullable return)
Scenario: List all members
Given multiple members exist in database When client executes query:
query {
members {
id
firstName
lastName
}
}
Then response contains array of all members And members are sorted by last name, first name When no members exist Then response contains empty array
Requirement: Mutation resolvers for modifying members
The system MUST provide GraphQL mutation resolvers for creating, updating, and deleting members.
Mutation operations:
createMember(input: CreateMemberInput!): Member!- Create new memberupdateMember(input: UpdateMemberInput!): Member!- Update existing memberdeleteMember(id: ID!): Boolean!- Delete member
Scenario: Create new member mutation
Given valid CreateMemberInput is provided When client executes mutation:
mutation {
createMember(input: {
firstName: "Jane"
lastName: "Smith"
street: "123 Main St"
zip: "12345"
city: "Springfield"
country: "USA"
email: "jane@example.com"
phone: "+15551234567"
}) {
id
firstName
email
}
}
Then new member is persisted to database And response contains newly created member with generated id And createdAt and updatedAt are populated
Scenario: Create member with minimal data (firstName only)
Given CreateMemberInput with only firstName is provided When client executes mutation:
mutation {
createMember(input: {
firstName: "John"
}) {
id
firstName
lastName
email
}
}
Then new member is created in database And firstName is "John" And lastName, email, phone, and address fields are null And response contains member with null optional fields
Scenario: Create member with invalid input
Given CreateMemberInput has invalid email format When client executes createMember mutation Then GraphQL error is returned And error message indicates validation failure And no database record is created
Scenario: Update existing member mutation
Given member exists with id=1 When client executes mutation:
mutation {
updateMember(input: {
id: 1
email: "newemail@example.com"
phone: "+15559876543"
}) {
id
email
phone
updatedAt
}
}
Then member email and phone are updated in database And updatedAt timestamp is refreshed And all other fields remain unchanged
Scenario: Update non-existent member
Given no member exists with id=999 When client executes updateMember with id=999 Then GraphQL error is returned And error message indicates "Member not found"
Scenario: Delete member mutation
Given member exists with id=1 When client executes mutation:
mutation {
deleteMember(id: 1)
}
Then member is removed from database And response returns true When query attempts to fetch deleted member Then response returns null
Scenario: Delete non-existent member
Given no member exists with id=999 When client executes deleteMember(id: 999) Then GraphQL error is returned And error message indicates "Member not found"
Requirement: FastAPI integration with GraphQL endpoint
The system MUST integrate Strawberry GraphQL schema with FastAPI application at /graphql path with GraphiQL playground enabled.
Scenario: GraphQL endpoint is mounted on FastAPI app
Given src/main.py creates FastAPI app When Strawberry schema is created from Query and Mutation classes Then GraphQLRouter is created with schema And router is mounted at /graphql path And GraphiQL is enabled for development
Scenario: GraphQL playground is accessible
Given FastAPI app is running When browser navigates to http://localhost:8000/graphql Then GraphiQL interface is displayed And schema documentation is available And queries can be executed interactively
Scenario: GraphQL endpoint accepts POST requests
Given GraphQL endpoint is configured When client sends POST to /graphql with query in body Then query is executed against schema And JSON response is returned with data or errors
Requirement: Error handling with meaningful messages
The system MUST provide clear, actionable error messages for validation failures, not found errors, and server errors.
Scenario: Input validation error provides field-level details
Given createMember mutation receives invalid email When mutation executes Then GraphQL error includes message "Invalid email format" And error path indicates which input field failed And HTTP status code is 400 (Bad Request)
Scenario: Not found error provides resource context
Given member query requests non-existent id=999 When query executes Then error message is "Member with ID 999 not found" And error type indicates resource not found
Scenario: Database error is handled gracefully
Given database connection fails during query When query executes Then GraphQL error indicates server error And internal error details are logged And client receives generic "Internal server error" message And HTTP status code is 500
Requirement: GraphQL Introspection Support
The GraphQL API SHALL support introspection queries that enable clients to discover the complete schema dynamically.
Scenario: Introspection query returns schema types
- GIVEN the GraphQL API is running
- WHEN a client executes an introspection query for
__schema - THEN the response SHALL include all types defined in the schema
- AND include queries, mutations, and object types
- AND include field names, types, and descriptions
Scenario: Introspection query returns type details
- GIVEN the GraphQL API is running
- WHEN a client executes an introspection query for
__type(name: "Member") - THEN the response SHALL include complete Member type details
- AND include all fields with their types
- AND include field descriptions where defined
Scenario: Introspection is enabled by default
- GIVEN the Strawberry GraphQL schema is configured
- WHEN the schema is created
- THEN introspection SHALL be enabled
- AND the
__schemaand__typequeries SHALL be available