@enfyra/mcp-server 0.1.53 → 0.1.54
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/lib/platform-operation-tools.d.ts +1 -0
- package/dist/lib/platform-operation-tools.js +111 -3
- package/dist/lib/platform-operation-tools.js.map +1 -1
- package/dist/lib/required-knowledge.js +5 -2
- package/dist/lib/required-knowledge.js.map +1 -1
- package/dist/lib/runtime-cache-socket.d.ts +2 -2
- package/dist/lib/runtime-cache-socket.js +67 -21
- package/dist/lib/runtime-cache-socket.js.map +1 -1
- package/dist/lib/toolset-filter.js +1 -0
- package/dist/lib/toolset-filter.js.map +1 -1
- package/package.json +2 -2
|
@@ -850,12 +850,34 @@ function toPascalIdentifier(value, fallback = 'Items') {
|
|
|
850
850
|
export function buildExtensionApiUsageSnippet(input = {}) {
|
|
851
851
|
const resource = String(input.resource || input.name || 'items');
|
|
852
852
|
const pascal = toPascalIdentifier(resource, 'Items');
|
|
853
|
-
const
|
|
854
|
-
const
|
|
853
|
+
const operation = String(input.operation || input.mode || input.intent || '').toLowerCase() || String(input.method || 'GET').toLowerCase();
|
|
854
|
+
const normalizedOperation = {
|
|
855
|
+
get: 'list',
|
|
856
|
+
read: 'list',
|
|
857
|
+
load: 'list',
|
|
858
|
+
post: 'create',
|
|
859
|
+
patch: 'update',
|
|
860
|
+
put: 'update',
|
|
861
|
+
del: 'delete',
|
|
862
|
+
remove: 'delete',
|
|
863
|
+
destroy: 'delete',
|
|
864
|
+
}[operation] || operation;
|
|
865
|
+
const defaultMethodByOperation = {
|
|
866
|
+
list: 'GET',
|
|
867
|
+
find_one: 'GET',
|
|
868
|
+
create: 'POST',
|
|
869
|
+
update: 'PATCH',
|
|
870
|
+
delete: 'DELETE',
|
|
871
|
+
batch_update: 'PATCH',
|
|
872
|
+
batch_delete: 'DELETE',
|
|
873
|
+
};
|
|
874
|
+
const method = String(input.method || defaultMethodByOperation[normalizedOperation] || 'GET').toUpperCase();
|
|
875
|
+
const rawPath = String(input.path || `/${resource}`);
|
|
876
|
+
const path = rawPath.replace(/\/:id\/?$/, '');
|
|
855
877
|
const responseName = input.responseName || `${resource}Response`;
|
|
856
878
|
const pendingName = input.pendingName || `${resource}Pending`;
|
|
857
879
|
const errorName = input.errorName || `${resource}Error`;
|
|
858
|
-
const executeName = input.executeName || (method === 'GET' ? `load${pascal}` : `${
|
|
880
|
+
const executeName = input.executeName || (method === 'GET' ? `load${pascal}` : `${normalizedOperation.replace(/(^|_)([a-z])/g, (_m, _p, ch) => ch.toUpperCase()).replace(/^./, (ch) => ch.toLowerCase())}${pascal}Api`);
|
|
859
881
|
const refreshName = input.refreshName || `refresh${pascal}`;
|
|
860
882
|
const options = [];
|
|
861
883
|
if (method !== 'GET')
|
|
@@ -879,6 +901,60 @@ export function buildExtensionApiUsageSnippet(input = {}) {
|
|
|
879
901
|
lines.push(`onMounted(() => { ${executeName}(); });`);
|
|
880
902
|
}
|
|
881
903
|
}
|
|
904
|
+
else if (normalizedOperation === 'create') {
|
|
905
|
+
const handlerName = input.handlerName || `create${pascal.replace(/s$/, '')}`;
|
|
906
|
+
const payloadName = input.payloadName || 'payload';
|
|
907
|
+
lines.push(...[
|
|
908
|
+
'',
|
|
909
|
+
`async function ${handlerName}(${payloadName}) {`,
|
|
910
|
+
` const response = await ${executeName}({ body: ${payloadName} });`,
|
|
911
|
+
' if (!response) return null;',
|
|
912
|
+
' return response;',
|
|
913
|
+
'}',
|
|
914
|
+
]);
|
|
915
|
+
}
|
|
916
|
+
else if (normalizedOperation === 'update') {
|
|
917
|
+
const handlerName = input.handlerName || `update${pascal.replace(/s$/, '')}`;
|
|
918
|
+
const recordName = input.recordName || 'record';
|
|
919
|
+
const bodyName = input.bodyName || 'body';
|
|
920
|
+
const idExpression = input.idExpression || `${recordName}.id`;
|
|
921
|
+
const bodyArg = bodyName === 'body' ? 'body' : `body: ${bodyName}`;
|
|
922
|
+
lines.push(...[
|
|
923
|
+
'',
|
|
924
|
+
`async function ${handlerName}(${recordName}, ${bodyName}) {`,
|
|
925
|
+
` const response = await ${executeName}({ id: ${idExpression}, ${bodyArg} });`,
|
|
926
|
+
' if (!response) return null;',
|
|
927
|
+
' return response;',
|
|
928
|
+
'}',
|
|
929
|
+
]);
|
|
930
|
+
}
|
|
931
|
+
else if (normalizedOperation === 'delete') {
|
|
932
|
+
const handlerName = input.handlerName || `delete${pascal.replace(/s$/, '')}`;
|
|
933
|
+
const recordName = input.recordName || 'record';
|
|
934
|
+
const idExpression = input.idExpression || `${recordName}.id`;
|
|
935
|
+
lines.push(...[
|
|
936
|
+
'',
|
|
937
|
+
`async function ${handlerName}(${recordName}) {`,
|
|
938
|
+
` const response = await ${executeName}({ id: ${idExpression} });`,
|
|
939
|
+
' if (!response) return null;',
|
|
940
|
+
' return response;',
|
|
941
|
+
'}',
|
|
942
|
+
]);
|
|
943
|
+
}
|
|
944
|
+
else if (normalizedOperation === 'batch_update' || normalizedOperation === 'batch_delete') {
|
|
945
|
+
const handlerName = input.handlerName || `${normalizedOperation === 'batch_update' ? 'update' : 'delete'}${pascal}Batch`;
|
|
946
|
+
const idsName = input.idsName || 'ids';
|
|
947
|
+
const bodyName = input.bodyName || 'body';
|
|
948
|
+
const args = normalizedOperation === 'batch_update' ? `{ ids: ${idsName}, body: ${bodyName} }` : `{ ids: ${idsName} }`;
|
|
949
|
+
lines.push(...[
|
|
950
|
+
'',
|
|
951
|
+
`async function ${handlerName}(${normalizedOperation === 'batch_update' ? `${idsName}, ${bodyName}` : idsName}) {`,
|
|
952
|
+
` const response = await ${executeName}(${args});`,
|
|
953
|
+
' if (!response) return null;',
|
|
954
|
+
' return response;',
|
|
955
|
+
'}',
|
|
956
|
+
]);
|
|
957
|
+
}
|
|
882
958
|
else {
|
|
883
959
|
const handlerName = input.handlerName || `${method.toLowerCase()}${pascal}Record`;
|
|
884
960
|
lines.push(...[
|
|
@@ -892,9 +968,11 @@ export function buildExtensionApiUsageSnippet(input = {}) {
|
|
|
892
968
|
}
|
|
893
969
|
return {
|
|
894
970
|
action: 'extension_api_usage_built',
|
|
971
|
+
operation: normalizedOperation,
|
|
895
972
|
snippet: lines.join('\n'),
|
|
896
973
|
contract: [
|
|
897
974
|
'useApi returns refs plus execute/refresh; it does not auto-run.',
|
|
975
|
+
'The useApi path is the base route string or a () => string getter; do not pass computed refs and do not put :id placeholders in the path.',
|
|
898
976
|
'Pass query/body as objects or computed objects, not JSON.stringify strings.',
|
|
899
977
|
'Read normal list rows from data.value?.data or from the direct execute() response.',
|
|
900
978
|
'For mutations, call execute({ body }), execute({ id, body }), execute({ id }), or execute({ ids }) from a user action.',
|
|
@@ -2848,6 +2926,36 @@ export function registerPlatformOperationTools(server, ENFYRA_API_URL) {
|
|
|
2848
2926
|
assertExtensionKnowledgeAck(extensionKnowledgeAckKey);
|
|
2849
2927
|
return jsonText(buildExtensionUiSnippet(kind, input));
|
|
2850
2928
|
});
|
|
2929
|
+
server.tool('build_extension_api_usage', [
|
|
2930
|
+
'Generate a contract-safe useApi snippet for Enfyra admin extensions.',
|
|
2931
|
+
'Use this instead of writing useApi calls from memory so route paths, execute({ id, body }), query/body objects, and mutation handlers follow the app composable contract.',
|
|
2932
|
+
'The tool returns code only; apply it with patch_extension_code or update_extension_code and then validate/save normally.',
|
|
2933
|
+
].join(' '), {
|
|
2934
|
+
operation: z.enum(['list', 'find_one', 'create', 'update', 'delete', 'batch_update', 'batch_delete']).default('list').describe('API usage pattern to generate. Reads use the base route with query objects; mutations append ids through execute options.'),
|
|
2935
|
+
resource: z.string().default('items').describe('Resource variable base name, e.g. notes, projects, messages.'),
|
|
2936
|
+
path: z.string().optional().describe('Base API route path such as /notes. Do not include /:id; the builder strips a trailing /:id if provided.'),
|
|
2937
|
+
queryExpression: z.string().optional().describe('Raw Vue expression for query object/computed. Do not JSON.stringify.'),
|
|
2938
|
+
bodyExpression: z.string().optional().describe('Raw Vue expression for default body object/computed when useful. Do not JSON.stringify.'),
|
|
2939
|
+
errorContext: z.string().optional().describe('Safe error context label for useApi error reporting.'),
|
|
2940
|
+
responseName: z.string().optional().describe('Optional data ref variable name.'),
|
|
2941
|
+
pendingName: z.string().optional().describe('Optional pending ref variable name.'),
|
|
2942
|
+
errorName: z.string().optional().describe('Optional error ref variable name.'),
|
|
2943
|
+
executeName: z.string().optional().describe('Optional execute alias name.'),
|
|
2944
|
+
refreshName: z.string().optional().describe('Optional refresh alias name.'),
|
|
2945
|
+
rowsName: z.string().optional().describe('Optional computed rows variable for list/find_one operations.'),
|
|
2946
|
+
handlerName: z.string().optional().describe('Optional generated handler function name for mutations.'),
|
|
2947
|
+
recordName: z.string().optional().describe('Record parameter name for update/delete handlers.'),
|
|
2948
|
+
payloadName: z.string().optional().describe('Payload parameter name for create handlers.'),
|
|
2949
|
+
bodyName: z.string().optional().describe('Body parameter name for update/batch_update handlers.'),
|
|
2950
|
+
idsName: z.string().optional().describe('Ids parameter name for batch handlers.'),
|
|
2951
|
+
idExpression: z.string().optional().describe('Raw id expression for update/delete handlers. Defaults to record.id.'),
|
|
2952
|
+
autoLoad: z.boolean().optional().default(true).describe('For reads, generate onMounted(() => execute()).'),
|
|
2953
|
+
onErrorExpression: z.string().optional().describe('Raw onError handler expression when custom handling is needed.'),
|
|
2954
|
+
extensionKnowledgeAckKey: extensionKnowledgeAckParam(z),
|
|
2955
|
+
}, async ({ extensionKnowledgeAckKey, ...input }) => {
|
|
2956
|
+
assertExtensionKnowledgeAck(extensionKnowledgeAckKey);
|
|
2957
|
+
return jsonText(buildExtensionApiUsageSnippet(input));
|
|
2958
|
+
});
|
|
2851
2959
|
server.tool('build_extension_drawer', [
|
|
2852
2960
|
'Generate a contract-safe CommonDrawer Vue snippet for Enfyra admin extensions.',
|
|
2853
2961
|
'Use this before writing or patching drawer/editing workflows so the model does not have to remember CommonDrawer slots, footer action props, full-width fields, or button type rules.',
|