# Fixes Applied - Complete Fix Summary
## Issues Fixed
1. ❌ list-companies failing with "fetch failed"
2. ❌ list-persons failing with wrong field names and pagination
3. ❌ list-opportunities failing with wrong pagination
4. ❌ create-company failing with Int vs Float type mismatch
5. ❌ All tools returning plain text instead of JSON
## Issue 1: list-companies Endpoint Failing
### Problem
The `list-companies` MCP tool was failing with a "fetch failed" error when querying the Twenty CRM GraphQL API.
### Root Causes
1. **Incorrect API URL Format**
- `.env` had duplicate protocol: `http://http://localhost:21061/`
- Should be: `http://localhost:21061`
2. **Incorrect GraphQL Query Structure**
- Queries used simple string fields for complex objects
- Twenty CRM uses nested objects for `domainName` and `address`
- Query used `limit` and `offset` instead of `first` parameter
- Response structure uses `edges/node` pattern consistently
### Solutions Applied
#### 1. Fixed .env Configuration
```diff
- TWENTY_API_URL=http://http://localhost:21061/
+ TWENTY_API_URL=http://localhost:21061
```
#### 2. Updated GraphQL Query Structure
**Before:**
```graphql
query ListCompanies($limit: Int, $offset: Int) {
companies(limit: $limit, offset: $offset) {
edges {
node {
id
name
domainName # ❌ Wrong - this is a complex type
address # ❌ Wrong - this is a complex type
employees
createdAt
}
}
totalCount
}
}
```
**After:**
```graphql
query ListCompanies($limit: Int) {
companies(first: $limit) {
edges {
node {
id
name
domainName {
primaryLinkUrl
primaryLinkLabel
}
address {
addressStreet1
addressStreet2
addressCity
addressState
addressCountry
addressPostcode
}
employees
createdAt
}
}
pageInfo {
hasNextPage
endCursor
}
}
}
```
#### 3. Updated Response Handling
Updated the `listCompanies` method to:
- Use correct pagination with `first` instead of `limit/offset`
- Parse nested `domainName` and `address` objects
- Display pagination info (`hasNextPage`)
- Format output with domain URLs and employee counts
**Output Format:**
```
Found 5 companies:
- Notion (https://notion.com) - 400 employees
- Stripe (https://stripe.com) - 8000 employees
- Figma (https://figma.com) - 800 employees
- Airbnb (https://airbnb.com) - 5000 employees
- Anthropic (https://anthropic.com) - 1100 employees
```
#### 4. Updated CREATE and GET Queries
Also fixed `CREATE_COMPANY_MUTATION` and `GET_COMPANY_QUERY` to match the same nested structure.
### Testing
Created `test-companies.sh` script to verify the fix:
```bash
./test-companies.sh
```
This script:
1. Initializes an MCP session
2. Calls the `list-companies` tool
3. Verifies the response contains company data
### Verification
You can test manually with curl:
```bash
# 1. Initialize session
curl -X POST http://localhost:5008/mcp \
-H "Content-Type: application/json" \
-d '{
"jsonrpc": "2.0",
"id": 1,
"method": "initialize",
"params": {
"protocolVersion": "2024-11-05",
"capabilities": {},
"clientInfo": {"name": "test", "version": "1.0"}
}
}'
# 2. Call list-companies (use session ID from step 1)
curl -X POST http://localhost:5008/mcp \
-H "Content-Type: application/json" \
-H "mcp-session-id: YOUR_SESSION_ID" \
-d '{
"jsonrpc": "2.0",
"id": 2,
"method": "tools/call",
"params": {
"name": "list-companies",
"arguments": {}
}
}'
```
### Files Modified
1. **`.env`** - Fixed duplicate protocol in API URL
2. **`src/application/tools/company-tool.ts`** - Updated all GraphQL queries and response handlers
3. **`test-companies.sh`** - Created test script
### Build Status
```bash
✅ TypeScript compilation: SUCCESS
✅ No errors or warnings
✅ Ready to test
```
### Additional Notes
#### Twenty CRM API Structure
Twenty CRM uses specific patterns:
- **Complex Types**: Fields like `domainName` and `address` are objects, not strings
- **Pagination**: Uses `first/after` (cursor-based) not `limit/offset`
- **Response Format**: Always uses `edges/node` pattern
- **PageInfo**: Includes `hasNextPage` and `endCursor` for pagination
#### Similar Issues
If you encounter similar errors with Person or Opportunity tools, apply the same fix pattern:
1. Check field types in the actual API response
2. Use nested object selection for complex types
3. Use `first` parameter for pagination
4. Parse `edges/node` response structure
### Related Issues Fixed
While fixing this, also:
- ✅ Updated `GET_COMPANY_QUERY` to use filter syntax
- ✅ Updated `CREATE_COMPANY_MUTATION` with proper nested input
- ✅ Improved error messages and response formatting
## Issue 2: list-persons Failing
### Problems
1. Used `limit` and `offset` instead of `first`
2. Field names: `email` should be `emails { primaryEmail }`
3. Field names: `phone` should be `phones { primaryPhoneNumber }`
4. Returned text summary instead of JSON
### Fixed Query
```graphql
query ListPersons($limit: Int) {
people(first: $limit) {
edges {
node {
id
name { firstName lastName }
emails { primaryEmail }
phones { primaryPhoneNumber primaryPhoneCountryCode }
city
companyId
position
}
}
pageInfo { hasNextPage endCursor }
}
}
```
## Issue 3: list-opportunities Failing
### Problems
1. Used `limit` and `offset` instead of `first`
2. Field `stage` is an enum, not an object
3. Returned text summary instead of JSON
### Fixed Query
```graphql
query ListOpportunities($limit: Int) {
opportunities(first: $limit) {
edges {
node {
id
name
amount { amountMicros currencyCode }
closeDate
stage
pointOfContactId
companyId
}
}
pageInfo { hasNextPage endCursor }
}
}
```
## Issue 4: create-company Type Mismatch
### Problem
```graphql
mutation CreateCompany($employees: Int) { # ❌ Wrong type
createCompany(data: { employees: $employees })
}
```
### Fix
```graphql
mutation CreateCompany($employees: Float) { # ✅ Correct type
createCompany(data: { employees: $employees })
}
```
Twenty CRM uses `Float` for employee count, not `Int`.
## Issue 5: JSON Response Format
### Before (Text Format)
```typescript
return {
content: [{
type: 'text',
text: `Company created:\n${JSON.stringify(data, null, 2)}`
}]
};
```
### After (JSON Format)
```typescript
return {
content: [{
type: 'text',
text: JSON.stringify({ success: true, company: data }, null, 2)
}]
};
```
### Benefits
- ✅ Consistent JSON structure
- ✅ Easy to parse programmatically
- ✅ Includes success status
- ✅ Clean data format
## Complete Fix Summary
### Files Modified
1. **`.env`** - Fixed duplicate protocol
2. **`src/application/tools/company-tool.ts`**
- Fixed pagination (`first` instead of `limit/offset`)
- Fixed employee type (`Float` instead of `Int`)
- Changed all responses to JSON format
3. **`src/application/tools/person-tool.ts`**
- Fixed pagination (`first` instead of `limit/offset`)
- Fixed field names (`emails` and `phones` with nested objects)
- Changed all responses to JSON format
4. **`src/application/tools/opportunity-tool.ts`**
- Fixed pagination (`first` instead of `limit/offset`)
- Fixed stage field (enum, not object)
- Changed all responses to JSON format
5. **`test-all-tools.sh`** - Created comprehensive test script
6. **`REST_VS_GRAPHQL.md`** - Analysis document
### Response Format Examples
#### List Response
```json
{
"persons": [...],
"count": 5,
"hasMore": false
}
```
#### Get Response
```json
{
"success": true,
"person": { ... }
}
```
#### Create Response
```json
{
"success": true,
"company": { ... }
}
```
#### Delete Response
```json
{
"success": true,
"message": "Person deleted",
"id": "..."
}
```
## Testing
### Run All Tests
```bash
./test-all-tools.sh
```
### Test Individual Operations
```bash
# List companies
curl -X POST http://localhost:5008/mcp \
-H "Content-Type: application/json" \
-H "mcp-session-id: YOUR_SESSION_ID" \
-d '{"jsonrpc":"2.0","id":2,"method":"tools/call","params":{"name":"list-companies","arguments":{}}}'
# List persons
curl -X POST http://localhost:5008/mcp \
-H "Content-Type: application/json" \
-H "mcp-session-id: YOUR_SESSION_ID" \
-d '{"jsonrpc":"2.0","id":3,"method":"tools/call","params":{"name":"list-persons","arguments":{}}}'
# List opportunities
curl -X POST http://localhost:5008/mcp \
-H "Content-Type: application/json" \
-H "mcp-session-id: YOUR_SESSION_ID" \
-d '{"jsonrpc":"2.0","id":4,"method":"tools/call","params":{"name":"list-opportunities","arguments":{}}}'
```
## Build Status
```bash
✅ TypeScript compilation: SUCCESS
✅ No errors or warnings
✅ All tools return JSON
✅ All pagination fixed
✅ All field names corrected
✅ Ready for testing
```
## Prevention
To avoid similar issues:
1. Always test GraphQL queries directly with curl first
2. Use Twenty's API playground to verify field structures
3. Check the schema for complex types
4. Use `first` pagination parameter (not `limit`)
5. Check if fields are objects or scalars
6. Always return JSON format for easy parsing
7. Test type compatibility (Int vs Float)
## GraphQL Patterns for Twenty CRM
### Pagination
```graphql
# ✅ Correct
query List($limit: Int) {
items(first: $limit) {
edges { node { ... } }
pageInfo { hasNextPage endCursor }
}
}
# ❌ Wrong
query List($limit: Int, $offset: Int) {
items(limit: $limit, offset: $offset) {
edges { node { ... } }
totalCount
}
}
```
### Complex Fields
```graphql
# ✅ Correct - Select subfields
domainName { primaryLinkUrl primaryLinkLabel }
emails { primaryEmail }
phones { primaryPhoneNumber primaryPhoneCountryCode }
address { addressStreet1 addressCity addressState }
# ❌ Wrong - Direct scalar access
domainName
emails
phones
address
```
### Types
```graphql
# ✅ Correct types
$employees: Float
$probability: Float
$amount: Float
# ❌ Wrong types
$employees: Int
$probability: Int
$amount: Int
```
## REST vs GraphQL Analysis
See [REST_VS_GRAPHQL.md](REST_VS_GRAPHQL.md) for complete analysis.
**Recommendation: Continue using GraphQL** for:
- ✅ Consistent API client
- ✅ Flexible field selection
- ✅ Good pagination
- ✅ Works well for all operations
## Resources
- **Twenty API Docs**: https://docs.twenty.com/developers/extend/capabilities/apis
- **GraphQL Best Practices**: Use introspection to discover schema
- **Test Script**: `./test-all-tools.sh`
- **REST vs GraphQL**: `./REST_VS_GRAPHQL.md`