mirror of
https://github.com/twentyhq/twenty
synced 2026-04-21 13:37:22 +00:00
parent
69649e97d4
commit
dcdf675000
10 changed files with 101 additions and 87 deletions
3
.gitignore
vendored
3
.gitignore
vendored
|
|
@ -46,4 +46,5 @@ dump.rdb
|
|||
.crowdin.yml
|
||||
.react-email/
|
||||
|
||||
mcp.json
|
||||
mcp.json
|
||||
/.junie/
|
||||
|
|
|
|||
|
|
@ -65,7 +65,7 @@ export const SettingsIntegrationMCP = () => {
|
|||
{
|
||||
mcpServers: {
|
||||
[serverName]: {
|
||||
type: 'remote',
|
||||
type: 'streamable-http',
|
||||
url: `${REACT_APP_SERVER_BASE_URL}${pathSuffix}`,
|
||||
headers: {
|
||||
Authorization: 'Bearer [API_KEY]',
|
||||
|
|
|
|||
|
|
@ -49,7 +49,7 @@ export class MCPMetadataService {
|
|||
}
|
||||
}
|
||||
|
||||
handleInitialize(requestId: string | number | null) {
|
||||
handleInitialize(requestId: string | number) {
|
||||
return wrapJsonRpcResponse(requestId, {
|
||||
result: {
|
||||
capabilities: {
|
||||
|
|
@ -61,63 +61,6 @@ export class MCPMetadataService {
|
|||
});
|
||||
}
|
||||
|
||||
get commonProperties() {
|
||||
return {
|
||||
fields: {
|
||||
type: 'array',
|
||||
items: {
|
||||
type: 'string',
|
||||
description:
|
||||
'Names of field properties to include in the response for field entities. ',
|
||||
examples: [
|
||||
'type',
|
||||
'name',
|
||||
'label',
|
||||
'description',
|
||||
'icon',
|
||||
'isCustom',
|
||||
'isActive',
|
||||
'isSystem',
|
||||
'isNullable',
|
||||
'createdAt',
|
||||
'updatedAt',
|
||||
'defaultValue',
|
||||
'options',
|
||||
'relation',
|
||||
],
|
||||
},
|
||||
description:
|
||||
'List of field names to select in the query for field entity. Strongly recommended to limit token usage and reduce response size. Use this to include only the properties you need.',
|
||||
},
|
||||
objects: {
|
||||
type: 'array',
|
||||
items: {
|
||||
type: 'string',
|
||||
description:
|
||||
'Object property names to include in the response for object entities.',
|
||||
examples: [
|
||||
'dataSourceId',
|
||||
'nameSingular',
|
||||
'namePlural',
|
||||
'labelSingular',
|
||||
'labelPlural',
|
||||
'description',
|
||||
'icon',
|
||||
'isCustom',
|
||||
'isActive',
|
||||
'isSystem',
|
||||
'createdAt',
|
||||
'updatedAt',
|
||||
'labelIdentifierFieldMetadataId',
|
||||
'imageIdentifierFieldMetadataId',
|
||||
],
|
||||
},
|
||||
description:
|
||||
'List of object properties to select in the query for object entities. Strongly recommended to limit token usage and reduce response size. Specify only the necessary properties to optimize your request.',
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
get tools() {
|
||||
return [
|
||||
...this.createToolsService.tools,
|
||||
|
|
@ -146,13 +89,14 @@ export class MCPMetadataService {
|
|||
});
|
||||
|
||||
return { result };
|
||||
} catch {
|
||||
} catch (err) {
|
||||
await this.metricsService.incrementCounter({
|
||||
key: MetricsKeys.AIToolExecutionFailed,
|
||||
attributes: {
|
||||
tool: request.body.params.name,
|
||||
},
|
||||
});
|
||||
throw err;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -170,7 +114,6 @@ export class MCPMetadataService {
|
|||
capabilities: {
|
||||
tools: { listChanged: false },
|
||||
},
|
||||
commonProperties: this.commonProperties,
|
||||
tools: Object.values(this.tools),
|
||||
},
|
||||
});
|
||||
|
|
@ -206,9 +149,15 @@ export class MCPMetadataService {
|
|||
);
|
||||
}
|
||||
|
||||
return this.listTools(request);
|
||||
if (request.body.method === 'tools/list') {
|
||||
return this.listTools(request);
|
||||
}
|
||||
|
||||
return wrapJsonRpcResponse(request.body.id ?? crypto.randomUUID(), {
|
||||
result: {},
|
||||
});
|
||||
} catch (error) {
|
||||
return wrapJsonRpcResponse(request.body.id, {
|
||||
return wrapJsonRpcResponse(request.body.id ?? crypto.randomUUID(), {
|
||||
error: {
|
||||
code: error.status || HttpStatus.INTERNAL_SERVER_ERROR,
|
||||
message:
|
||||
|
|
|
|||
|
|
@ -25,10 +25,56 @@ export class MCPMetadataToolsService {
|
|||
properties: {
|
||||
...schema.properties,
|
||||
fields: {
|
||||
$ref: '#/result/commonProperties/fields',
|
||||
type: 'array',
|
||||
items: {
|
||||
type: 'string',
|
||||
description:
|
||||
'Names of field properties to include in the response for field entities.',
|
||||
examples: [
|
||||
'type',
|
||||
'name',
|
||||
'label',
|
||||
'description',
|
||||
'icon',
|
||||
'isCustom',
|
||||
'isActive',
|
||||
'isSystem',
|
||||
'isNullable',
|
||||
'createdAt',
|
||||
'updatedAt',
|
||||
'defaultValue',
|
||||
'options',
|
||||
'relation',
|
||||
],
|
||||
},
|
||||
description:
|
||||
'List of field names to select in the query for field entity. Strongly recommended to limit token usage and reduce response size. Use this to include only the properties you need.',
|
||||
},
|
||||
objects: {
|
||||
$ref: '#/result/commonProperties/objects',
|
||||
type: 'array',
|
||||
items: {
|
||||
type: 'string',
|
||||
description:
|
||||
'Object property names to include in the response for object entities.',
|
||||
examples: [
|
||||
'dataSourceId',
|
||||
'nameSingular',
|
||||
'namePlural',
|
||||
'labelSingular',
|
||||
'labelPlural',
|
||||
'description',
|
||||
'icon',
|
||||
'isCustom',
|
||||
'isActive',
|
||||
'isSystem',
|
||||
'createdAt',
|
||||
'updatedAt',
|
||||
'labelIdentifierFieldMetadataId',
|
||||
'imageIdentifierFieldMetadataId',
|
||||
],
|
||||
},
|
||||
description:
|
||||
'List of object properties to select in the query for object entities. Strongly recommended to limit token usage and reduce response size. Specify only the necessary properties to optimize your request.',
|
||||
},
|
||||
},
|
||||
};
|
||||
|
|
|
|||
|
|
@ -24,17 +24,26 @@ export class UpdateToolsService {
|
|||
inputSchema:
|
||||
this.mCPMetadataToolsService.mergeSchemaWithCommonProperties({
|
||||
...validationSchemaManager.getSchemas().UpdateOneFieldMetadataInput,
|
||||
properties: omit(
|
||||
validationSchemaManager.getSchemas().FieldMetadataDTO.properties,
|
||||
[
|
||||
'id',
|
||||
'type',
|
||||
'createdAt',
|
||||
'updatedAt',
|
||||
'isCustom',
|
||||
'standardOverrides',
|
||||
],
|
||||
),
|
||||
required: ['id'],
|
||||
properties: {
|
||||
...omit(
|
||||
validationSchemaManager.getSchemas().UpdateOneFieldMetadataInput
|
||||
.properties,
|
||||
['update'],
|
||||
),
|
||||
...omit(
|
||||
validationSchemaManager.getSchemas().FieldMetadataDTO
|
||||
.properties,
|
||||
[
|
||||
'id',
|
||||
'type',
|
||||
'createdAt',
|
||||
'updatedAt',
|
||||
'isCustom',
|
||||
'standardOverrides',
|
||||
],
|
||||
),
|
||||
},
|
||||
}),
|
||||
execute: (request: Request) => this.execute(request, 'fields'),
|
||||
},
|
||||
|
|
@ -42,9 +51,19 @@ export class UpdateToolsService {
|
|||
name: 'update-object-metadata',
|
||||
description: 'Update an object metadata',
|
||||
inputSchema:
|
||||
this.mCPMetadataToolsService.mergeSchemaWithCommonProperties(
|
||||
validationSchemaManager.getSchemas().UpdateOneObjectInput,
|
||||
),
|
||||
this.mCPMetadataToolsService.mergeSchemaWithCommonProperties({
|
||||
...validationSchemaManager.getSchemas().UpdateOneObjectInput,
|
||||
required: ['id'],
|
||||
properties: {
|
||||
...omit(
|
||||
validationSchemaManager.getSchemas().UpdateOneObjectInput
|
||||
.properties,
|
||||
['update'],
|
||||
),
|
||||
...validationSchemaManager.getSchemas().UpdateObjectPayload
|
||||
.properties,
|
||||
},
|
||||
}),
|
||||
execute: (request: Request) => this.execute(request, 'objects'),
|
||||
},
|
||||
];
|
||||
|
|
|
|||
|
|
@ -52,7 +52,6 @@ export const fetchMetadataFields = (
|
|||
const objectsSelection =
|
||||
selector?.objects?.join('\n') ??
|
||||
`
|
||||
dataSourceId
|
||||
nameSingular
|
||||
namePlural
|
||||
labelSingular
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
export const MCP_SERVER_METADATA = {
|
||||
metadata: {
|
||||
info: '📦 Objects structure your business entities in Twenty. **Standard Objects** (e.g. People, Companies, Opportunities) are built‑in, pre‑configured 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.',
|
||||
info: 'Objects structure your business entities in Twenty. **Standard Objects** (e.g. People, Companies, Opportunities) are built‑in, pre‑configured 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: {
|
||||
|
|
|
|||
|
|
@ -27,5 +27,5 @@ export class JsonRpc {
|
|||
|
||||
@IsOptional()
|
||||
@Validate(IsNumberOrString)
|
||||
id: string | number | null;
|
||||
id: string | number;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -39,7 +39,7 @@ export class McpService {
|
|||
}
|
||||
}
|
||||
|
||||
handleInitialize(requestId: string | number | null) {
|
||||
handleInitialize(requestId: string | number) {
|
||||
return wrapJsonRpcResponse(requestId, {
|
||||
result: {
|
||||
capabilities: {
|
||||
|
|
@ -129,7 +129,7 @@ export class McpService {
|
|||
}
|
||||
|
||||
private async handleToolCall(
|
||||
id: string | number | null,
|
||||
id: string | number,
|
||||
toolSet: ToolSet,
|
||||
params: Record<string, unknown>,
|
||||
) {
|
||||
|
|
@ -161,7 +161,7 @@ export class McpService {
|
|||
);
|
||||
}
|
||||
|
||||
private handleToolsListing(id: string | number | null, toolSet: ToolSet) {
|
||||
private handleToolsListing(id: string | number, toolSet: ToolSet) {
|
||||
const toolsArray = Object.entries(toolSet)
|
||||
.filter(([, def]) => !!def.parameters.jsonSchema)
|
||||
.map(([name, def]) => ({
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
import { MCP_SERVER_METADATA } from 'src/engine/core-modules/ai/constants/mcp.const';
|
||||
|
||||
export const wrapJsonRpcResponse = (
|
||||
id: string | number | null | undefined = null,
|
||||
id: string | number,
|
||||
payload:
|
||||
| Record<'result', Record<string, unknown>>
|
||||
| Record<'error', Record<string, unknown>>,
|
||||
|
|
|
|||
Loading…
Reference in a new issue