feat(ai): add mcp-metadata (#13150)

Co-authored-by: Félix Malfait <felix@twenty.com>
This commit is contained in:
Antoine Moreaux
2025-07-16 21:32:32 +02:00
committed by GitHub
parent b25f50e288
commit 11abe5440b
50 changed files with 1288 additions and 230 deletions

View File

@ -1,4 +1,7 @@
export const MCP_SERVER_METADATA = {
metadata: {
info: '📦 Objects structure your business entities in Twenty. **Standard Objects** (e.g. People, Companies, Opportunities) are builtin, preconfigured data models. **Custom Objects** let you define entities specific to your needs (like Rockets, Properties, etc.). **Fields** work like spreadsheet columns and can be standard or custom. Always use the `fields` and `objects` parameters to select only the data you need—this **strongly reduces response size and token usage**, improving performance.',
},
protocolVersion: '2024-11-05',
serverInfo: {
name: 'Twenty MCP Server',

View File

@ -20,7 +20,10 @@ export class JsonRpc {
@IsOptional()
@IsObject()
params?: Record<string, unknown>;
params?: {
name: string;
arguments: unknown;
};
@IsOptional()
@Validate(IsNumberOrString)

View File

@ -110,6 +110,7 @@ export class McpService {
userWorkspaceId,
apiKey,
);
const toolSet = await this.toolService.listTools(roleId, workspace.id);
if (method === 'tools/call' && params) {

View File

@ -5,14 +5,19 @@ export const wrapJsonRpcResponse = (
payload:
| Record<'result', Record<string, unknown>>
| Record<'error', Record<string, unknown>>,
omitMetadata = false,
) => {
const body =
'result' in payload
? {
result: { ...payload.result, ...MCP_SERVER_METADATA },
result: omitMetadata
? payload.result
: { ...payload.result, ...MCP_SERVER_METADATA },
}
: {
error: { ...payload.error, ...MCP_SERVER_METADATA },
error: omitMetadata
? payload.error
: { ...payload.error, ...MCP_SERVER_METADATA },
};
return {

View File

@ -21,4 +21,6 @@ export enum MetricsKeys {
WorkflowRunFailed = 'workflow-run/failed',
WorkflowRunFailedThrottled = 'workflow-run/failed/throttled',
WorkflowRunFailedToEnqueue = 'workflow-run/failed/to-enqueue',
AIToolExecutionFailed = 'ai-tool-execution/failed',
AIToolExecutionSucceeded = 'ai-tool-execution/succeeded',
}

View File

@ -50,8 +50,8 @@ export class OpenApiService {
async generateCoreSchema(request: Request): Promise<OpenAPIV3_1.Document> {
const baseUrl = getServerUrl(
request,
this.twentyConfigService.get('SERVER_URL'),
`${request.protocol}://${request.get('host')}`,
);
const schema = baseSchema('core', baseUrl);
@ -135,8 +135,8 @@ export class OpenApiService {
request: Request,
): Promise<OpenAPIV3_1.Document> {
const baseUrl = getServerUrl(
request,
this.twentyConfigService.get('SERVER_URL'),
`${request.protocol}://${request.get('host')}`,
);
const schema = baseSchema('metadata', baseUrl);