/**
* Tool: get_workspace_schema
*
* Get Attio workspace schema with flexible scope options:
* - summary (default): All objects with key attributes only
* - full: All objects with all attributes
* - object: One specific object with all attributes
* - list: One specific list with all attributes
*/
import type { CallToolResult, Tool } from '@modelcontextprotocol/sdk/types.js';
import {
getWorkspaceSchema,
filterToKeyAttributes,
type ObjectSchema,
} from '../lib/schema-cache.js';
import {
handleToolError,
createSuccessResponse,
} from '../utils/error-handler.js';
import { ConfigurationError, ValidationError } from '../utils/errors.js';
/**
* Tool definition
*/
export const getWorkspaceSchemaTool: Tool = {
name: 'get_workspace_schema',
description:
'Get Attio workspace schema. Default returns summary with key fields and select options. Use scope="full" for all fields, or scope="object"/"list" for specific items with all fields.',
inputSchema: {
type: 'object',
properties: {
scope: {
type: 'string',
enum: ['summary', 'full', 'object', 'list'],
description:
'What to retrieve: "summary" (default - key fields only), "full" (all fields), "object" (one object with all fields), "list" (one list with all fields)',
default: 'summary',
},
object_slug: {
type: 'string',
description:
'Object slug (required if scope="object"). Examples: companies, people, funds, investment_opportunities, lp_opportunities',
},
list_slug: {
type: 'string',
description: 'List slug (required if scope="list")',
},
force_reload: {
type: 'boolean',
description:
'Bypass cache and fetch fresh schema from Attio API (default: false)',
default: false,
},
},
required: [],
},
};
/**
* Tool handler
*/
export async function handleGetWorkspaceSchema(args: {
scope?: string;
object_slug?: string;
list_slug?: string;
force_reload?: boolean;
}): Promise<CallToolResult> {
try {
const scope = args.scope || 'summary';
const forceReload = args.force_reload || false;
// Get API key
const apiKey = process.env['ATTIO_API_KEY'];
if (!apiKey) {
throw new ConfigurationError('ATTIO_API_KEY not configured');
}
// Get full schema from shared cache
const fullSchema = await getWorkspaceSchema(apiKey, forceReload);
let result: any;
if (scope === 'summary') {
// Filter to key attributes only
result = filterToKeyAttributes(fullSchema);
} else if (scope === 'full') {
// Return full schema with metadata
result = {
...fullSchema,
view: 'full',
};
// Add showing: 'all' to each object
for (const obj of Object.values(result.objects) as ObjectSchema[]) {
obj.total_attributes = obj.attributes.length;
obj.showing = 'all';
}
} else if (scope === 'object') {
// Validate object_slug is provided
if (!args.object_slug) {
throw new ValidationError(
'object_slug is required when scope="object"',
'object_slug'
);
}
// Check if object exists
const objectSchema = fullSchema.objects[args.object_slug];
if (!objectSchema) {
throw new ValidationError(
`Object "${args.object_slug}" not found. Available objects: ${Object.keys(fullSchema.objects).join(', ')}`,
'object_slug'
);
}
// Return specific object with all attributes
result = {
...objectSchema,
total_attributes: objectSchema.attributes.length,
showing: 'all',
};
} else if (scope === 'list') {
// Validate list_slug is provided
if (!args.list_slug) {
throw new ValidationError(
'list_slug is required when scope="list"',
'list_slug'
);
}
// Check if list exists
const listSchema = fullSchema.lists?.[args.list_slug];
if (!listSchema) {
const availableLists = fullSchema.lists
? Object.keys(fullSchema.lists).join(', ')
: 'No lists are currently defined in the workspace';
throw new ValidationError(
`List "${args.list_slug}" not found. ${availableLists}`,
'list_slug'
);
}
// Return specific list with all attributes
result = {
...listSchema,
total_attributes: listSchema.attributes.length,
showing: 'all',
};
} else {
// Invalid scope
throw new ValidationError(
`Invalid scope: "${scope}". Valid scopes: summary, full, object, list`,
'scope'
);
}
// Add helpful note about tool parameter mapping
if (scope === 'summary' || scope === 'full' || scope === 'object') {
result._note =
'Tool parameter names may differ from attribute slugs for readability (e.g., company_id maps to "company", fund_id maps to "fund_5"). Tools handle this mapping internally.';
}
return createSuccessResponse(result);
} catch (error) {
return handleToolError(error, 'get_workspace_schema');
}
}