@netlinksinc/odoo-mcp 0.1.0 → 0.1.1
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/tools/action.d.ts.map +1 -1
- package/dist/tools/action.js +26 -110
- package/dist/tools/action.js.map +1 -1
- package/dist/tools/execute.d.ts.map +1 -1
- package/dist/tools/execute.js +24 -108
- package/dist/tools/execute.js.map +1 -1
- package/dist/tools/introspect.d.ts.map +1 -1
- package/dist/tools/introspect.js +24 -107
- package/dist/tools/introspect.js.map +1 -1
- package/dist/tools/orm.d.ts +5 -7
- package/dist/tools/orm.d.ts.map +1 -1
- package/dist/tools/orm.js +44 -81
- package/dist/tools/orm.js.map +1 -1
- package/dist/tools/report.d.ts.map +1 -1
- package/dist/tools/report.js +19 -45
- package/dist/tools/report.js.map +1 -1
- package/package.json +2 -2
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"action.d.ts","sourceRoot":"","sources":["../../src/tools/action.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AACzE,OAAO,EAAa,KAAK,WAAW,EAAgB,MAAM,0BAA0B,CAAC;AACrF,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,0BAA0B,CAAC;AAI3D,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,cAAc,CAAC;
|
|
1
|
+
{"version":3,"file":"action.d.ts","sourceRoot":"","sources":["../../src/tools/action.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AACzE,OAAO,EAAa,KAAK,WAAW,EAAgB,MAAM,0BAA0B,CAAC;AACrF,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,0BAA0B,CAAC;AAI3D,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,cAAc,CAAC;AAG3C,wBAAgB,kBAAkB,CAChC,MAAM,EAAE,SAAS,EACjB,MAAM,EAAE,UAAU,EAClB,OAAO,EAAE,WAAW,EACpB,MAAM,EAAE,MAAM,GACb,IAAI,CA4EN"}
|
package/dist/tools/action.js
CHANGED
|
@@ -2,145 +2,60 @@ import { OdooError, sanitizeArgs } from '@netlinksinc/odoo-client';
|
|
|
2
2
|
import { buildContext, validateCompanySubset } from '../context.js';
|
|
3
3
|
import { formatMcpError } from '../errors.js';
|
|
4
4
|
import { callActionSchema } from './schemas.js';
|
|
5
|
-
function inputValidationError(message) {
|
|
6
|
-
return {
|
|
7
|
-
isError: true,
|
|
8
|
-
content: [
|
|
9
|
-
{
|
|
10
|
-
type: 'text',
|
|
11
|
-
text: JSON.stringify({ error_type: 'InputValidationError', message }),
|
|
12
|
-
},
|
|
13
|
-
],
|
|
14
|
-
};
|
|
15
|
-
}
|
|
16
5
|
export function registerActionTool(server, client, session, logger) {
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
const
|
|
22
|
-
|
|
23
|
-
logger.toolCall({
|
|
24
|
-
tool: 'odoo_call_action',
|
|
25
|
-
args_sanitized: sanitizeArgs('odoo_call_action', args),
|
|
26
|
-
latency_ms: Date.now() - start,
|
|
27
|
-
status: 'error',
|
|
28
|
-
error: 'InputValidationError',
|
|
29
|
-
});
|
|
30
|
-
return inputValidationError(parsed.error.message);
|
|
31
|
-
}
|
|
32
|
-
const data = parsed.data;
|
|
33
|
-
// Step 2: validate company subset if provided
|
|
34
|
-
if (data.allowed_company_ids !== undefined) {
|
|
35
|
-
try {
|
|
36
|
-
validateCompanySubset(data.allowed_company_ids, session.allowedCompanyIds);
|
|
37
|
-
}
|
|
38
|
-
catch (e) {
|
|
39
|
-
if (e instanceof OdooError) {
|
|
40
|
-
const latency_ms = Date.now() - start;
|
|
41
|
-
logger.toolCall({
|
|
42
|
-
tool: 'odoo_call_action',
|
|
43
|
-
args_sanitized: sanitizeArgs('odoo_call_action', args),
|
|
44
|
-
latency_ms,
|
|
45
|
-
status: 'error',
|
|
46
|
-
error: e.message,
|
|
47
|
-
});
|
|
48
|
-
return {
|
|
49
|
-
isError: true,
|
|
50
|
-
content: [
|
|
51
|
-
{
|
|
52
|
-
type: 'text',
|
|
53
|
-
text: JSON.stringify(formatMcpError(e)),
|
|
54
|
-
},
|
|
55
|
-
],
|
|
56
|
-
};
|
|
57
|
-
}
|
|
58
|
-
// Non-OdooError from company validation — log and return as InternalError.
|
|
59
|
-
const message = e instanceof Error ? e.message : String(e);
|
|
60
|
-
const latency_ms = Date.now() - start;
|
|
61
|
-
logger.toolCall({
|
|
62
|
-
tool: 'odoo_call_action',
|
|
63
|
-
args_sanitized: sanitizeArgs('odoo_call_action', args),
|
|
64
|
-
latency_ms,
|
|
65
|
-
status: 'error',
|
|
66
|
-
error: 'InternalError',
|
|
67
|
-
});
|
|
68
|
-
return {
|
|
69
|
-
isError: true,
|
|
70
|
-
content: [
|
|
71
|
-
{
|
|
72
|
-
type: 'text',
|
|
73
|
-
text: JSON.stringify({
|
|
74
|
-
error_type: 'InternalError',
|
|
75
|
-
message: 'unexpected error',
|
|
76
|
-
detail: message,
|
|
77
|
-
}),
|
|
78
|
-
},
|
|
79
|
-
],
|
|
80
|
-
};
|
|
81
|
-
}
|
|
82
|
-
}
|
|
83
|
-
// Step 3: merge caller context with company context.
|
|
84
|
-
// buildContext re-applies uid/company_id/allowed_company_ids AFTER extraContext
|
|
85
|
-
// so caller-supplied context CANNOT override identity (US-5 AC-4 + US-7 AC-7).
|
|
86
|
-
const context = buildContext(session, {
|
|
87
|
-
allowed_company_ids: data.allowed_company_ids,
|
|
88
|
-
active_company_id: data.active_company_id,
|
|
89
|
-
}, data.context);
|
|
90
|
-
// Step 4: call client.callAction
|
|
6
|
+
server.registerTool('odoo_call_action', {
|
|
7
|
+
description: 'Call a named server action (a method) on a model. Caller-supplied `context` merges with session context but cannot override `uid` or `company_id`.',
|
|
8
|
+
inputSchema: callActionSchema.shape,
|
|
9
|
+
}, async (args) => {
|
|
10
|
+
const t0 = Date.now();
|
|
11
|
+
const rawArgs = args;
|
|
91
12
|
try {
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
13
|
+
if (args.allowed_company_ids) {
|
|
14
|
+
validateCompanySubset(args.allowed_company_ids, session.allowedCompanyIds);
|
|
15
|
+
}
|
|
16
|
+
// buildContext re-applies session-authoritative fields AFTER extraContext
|
|
17
|
+
// so caller-supplied context cannot override identity (US-5 AC-4 + US-7 AC-7).
|
|
18
|
+
const context = buildContext(session, {
|
|
19
|
+
allowed_company_ids: args.allowed_company_ids,
|
|
20
|
+
active_company_id: args.active_company_id,
|
|
21
|
+
}, args.context);
|
|
22
|
+
const result = await client.callAction(args.model, args.ids, args.action_name, context);
|
|
95
23
|
logger.toolCall({
|
|
96
24
|
tool: 'odoo_call_action',
|
|
97
|
-
args_sanitized: sanitizeArgs('odoo_call_action',
|
|
98
|
-
latency_ms,
|
|
25
|
+
args_sanitized: sanitizeArgs('odoo_call_action', rawArgs),
|
|
26
|
+
latency_ms: Date.now() - t0,
|
|
99
27
|
status: 'ok',
|
|
100
28
|
});
|
|
101
29
|
return {
|
|
30
|
+
content: [{ type: 'text', text: JSON.stringify(result) }],
|
|
102
31
|
isError: false,
|
|
103
|
-
content: [
|
|
104
|
-
{
|
|
105
|
-
type: 'text',
|
|
106
|
-
text: JSON.stringify(result),
|
|
107
|
-
},
|
|
108
|
-
],
|
|
109
32
|
};
|
|
110
33
|
}
|
|
111
34
|
catch (e) {
|
|
112
|
-
|
|
35
|
+
const latency_ms = Date.now() - t0;
|
|
113
36
|
if (e instanceof OdooError) {
|
|
114
|
-
const
|
|
37
|
+
const formatted = formatMcpError(e);
|
|
115
38
|
logger.toolCall({
|
|
116
39
|
tool: 'odoo_call_action',
|
|
117
|
-
args_sanitized: sanitizeArgs('odoo_call_action',
|
|
40
|
+
args_sanitized: sanitizeArgs('odoo_call_action', rawArgs),
|
|
118
41
|
latency_ms,
|
|
119
42
|
status: 'error',
|
|
120
|
-
error:
|
|
43
|
+
error: formatted.error_type,
|
|
121
44
|
});
|
|
122
45
|
return {
|
|
46
|
+
content: [{ type: 'text', text: JSON.stringify(formatted) }],
|
|
123
47
|
isError: true,
|
|
124
|
-
content: [
|
|
125
|
-
{
|
|
126
|
-
type: 'text',
|
|
127
|
-
text: JSON.stringify(formatMcpError(e)),
|
|
128
|
-
},
|
|
129
|
-
],
|
|
130
48
|
};
|
|
131
49
|
}
|
|
132
|
-
// Step 7: Non-OdooError — unexpected exception. Log + return as InternalError-shaped.
|
|
133
50
|
const message = e instanceof Error ? e.message : String(e);
|
|
134
|
-
const latency_ms = Date.now() - start;
|
|
135
51
|
logger.toolCall({
|
|
136
52
|
tool: 'odoo_call_action',
|
|
137
|
-
args_sanitized: sanitizeArgs('odoo_call_action',
|
|
53
|
+
args_sanitized: sanitizeArgs('odoo_call_action', rawArgs),
|
|
138
54
|
latency_ms,
|
|
139
55
|
status: 'error',
|
|
140
56
|
error: 'InternalError',
|
|
141
57
|
});
|
|
142
58
|
return {
|
|
143
|
-
isError: true,
|
|
144
59
|
content: [
|
|
145
60
|
{
|
|
146
61
|
type: 'text',
|
|
@@ -151,6 +66,7 @@ export function registerActionTool(server, client, session, logger) {
|
|
|
151
66
|
}),
|
|
152
67
|
},
|
|
153
68
|
],
|
|
69
|
+
isError: true,
|
|
154
70
|
};
|
|
155
71
|
}
|
|
156
72
|
});
|
package/dist/tools/action.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"action.js","sourceRoot":"","sources":["../../src/tools/action.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,SAAS,EAAoB,YAAY,EAAE,MAAM,0BAA0B,CAAC;AAGrF,OAAO,EAAE,YAAY,EAAE,qBAAqB,EAAE,MAAM,eAAe,CAAC;AACpE,OAAO,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;AAE9C,OAAO,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAC;AAEhD,
|
|
1
|
+
{"version":3,"file":"action.js","sourceRoot":"","sources":["../../src/tools/action.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"execute.d.ts","sourceRoot":"","sources":["../../src/tools/execute.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AACzE,OAAO,EAA2B,KAAK,WAAW,EAAgB,MAAM,0BAA0B,CAAC;AACnG,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,0BAA0B,CAAC;AAI3D,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,cAAc,CAAC;
|
|
1
|
+
{"version":3,"file":"execute.d.ts","sourceRoot":"","sources":["../../src/tools/execute.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AACzE,OAAO,EAA2B,KAAK,WAAW,EAAgB,MAAM,0BAA0B,CAAC;AACnG,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,0BAA0B,CAAC;AAI3D,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,cAAc,CAAC;AAG3C,wBAAgB,mBAAmB,CACjC,MAAM,EAAE,SAAS,EACjB,MAAM,EAAE,UAAU,EAClB,OAAO,EAAE,WAAW,EACpB,MAAM,EAAE,MAAM,GACb,IAAI,CA4EN"}
|
package/dist/tools/execute.js
CHANGED
|
@@ -2,143 +2,58 @@ import { OdooError, sanitizeArgs } from '@netlinksinc/odoo-client';
|
|
|
2
2
|
import { buildContext, validateCompanySubset } from '../context.js';
|
|
3
3
|
import { formatMcpError } from '../errors.js';
|
|
4
4
|
import { executeSchema } from './schemas.js';
|
|
5
|
-
function inputValidationError(message) {
|
|
6
|
-
return {
|
|
7
|
-
isError: true,
|
|
8
|
-
content: [
|
|
9
|
-
{
|
|
10
|
-
type: 'text',
|
|
11
|
-
text: JSON.stringify({ error_type: 'InputValidationError', message }),
|
|
12
|
-
},
|
|
13
|
-
],
|
|
14
|
-
};
|
|
15
|
-
}
|
|
16
5
|
export function registerExecuteTool(server, client, session, logger) {
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
const
|
|
22
|
-
|
|
23
|
-
logger.toolCall({
|
|
24
|
-
tool: 'odoo_execute',
|
|
25
|
-
args_sanitized: sanitizeArgs('odoo_execute', args),
|
|
26
|
-
latency_ms: Date.now() - start,
|
|
27
|
-
status: 'error',
|
|
28
|
-
error: 'InputValidationError',
|
|
29
|
-
});
|
|
30
|
-
return inputValidationError(parsed.error.message);
|
|
31
|
-
}
|
|
32
|
-
const data = parsed.data;
|
|
33
|
-
// Step 2: validate company subset if provided
|
|
34
|
-
if (data.allowed_company_ids !== undefined) {
|
|
35
|
-
try {
|
|
36
|
-
validateCompanySubset(data.allowed_company_ids, session.allowedCompanyIds);
|
|
37
|
-
}
|
|
38
|
-
catch (e) {
|
|
39
|
-
if (e instanceof OdooError) {
|
|
40
|
-
const latency_ms = Date.now() - start;
|
|
41
|
-
logger.toolCall({
|
|
42
|
-
tool: 'odoo_execute',
|
|
43
|
-
args_sanitized: sanitizeArgs('odoo_execute', args),
|
|
44
|
-
latency_ms,
|
|
45
|
-
status: 'error',
|
|
46
|
-
error: e.message,
|
|
47
|
-
});
|
|
48
|
-
return {
|
|
49
|
-
isError: true,
|
|
50
|
-
content: [
|
|
51
|
-
{
|
|
52
|
-
type: 'text',
|
|
53
|
-
text: JSON.stringify(formatMcpError(e)),
|
|
54
|
-
},
|
|
55
|
-
],
|
|
56
|
-
};
|
|
57
|
-
}
|
|
58
|
-
// Non-OdooError from company validation — log and return as InternalError.
|
|
59
|
-
const message = e instanceof Error ? e.message : String(e);
|
|
60
|
-
const latency_ms = Date.now() - start;
|
|
61
|
-
logger.toolCall({
|
|
62
|
-
tool: 'odoo_execute',
|
|
63
|
-
args_sanitized: sanitizeArgs('odoo_execute', args),
|
|
64
|
-
latency_ms,
|
|
65
|
-
status: 'error',
|
|
66
|
-
error: 'InternalError',
|
|
67
|
-
});
|
|
68
|
-
return {
|
|
69
|
-
isError: true,
|
|
70
|
-
content: [
|
|
71
|
-
{
|
|
72
|
-
type: 'text',
|
|
73
|
-
text: JSON.stringify({
|
|
74
|
-
error_type: 'InternalError',
|
|
75
|
-
message: 'unexpected error',
|
|
76
|
-
detail: message,
|
|
77
|
-
}),
|
|
78
|
-
},
|
|
79
|
-
],
|
|
80
|
-
};
|
|
81
|
-
}
|
|
82
|
-
}
|
|
83
|
-
// Step 3: build context
|
|
84
|
-
const context = buildContext(session, {
|
|
85
|
-
allowed_company_ids: data.allowed_company_ids,
|
|
86
|
-
active_company_id: data.active_company_id,
|
|
87
|
-
});
|
|
88
|
-
// Step 4: call client.execute
|
|
6
|
+
server.registerTool('odoo_execute', {
|
|
7
|
+
description: 'Call any model method (execute_kw). Use this for operations not covered by the typed CRUD tools. Model and method are regex-validated.',
|
|
8
|
+
inputSchema: executeSchema.shape,
|
|
9
|
+
}, async (args) => {
|
|
10
|
+
const t0 = Date.now();
|
|
11
|
+
const rawArgs = args;
|
|
89
12
|
try {
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
13
|
+
if (args.allowed_company_ids) {
|
|
14
|
+
validateCompanySubset(args.allowed_company_ids, session.allowedCompanyIds);
|
|
15
|
+
}
|
|
16
|
+
const context = buildContext(session, {
|
|
17
|
+
allowed_company_ids: args.allowed_company_ids,
|
|
18
|
+
active_company_id: args.active_company_id,
|
|
19
|
+
});
|
|
20
|
+
const result = await client.execute(args.model, args.method, args.args, args.kwargs, context);
|
|
93
21
|
logger.toolCall({
|
|
94
22
|
tool: 'odoo_execute',
|
|
95
|
-
args_sanitized: sanitizeArgs('odoo_execute',
|
|
96
|
-
latency_ms,
|
|
23
|
+
args_sanitized: sanitizeArgs('odoo_execute', rawArgs),
|
|
24
|
+
latency_ms: Date.now() - t0,
|
|
97
25
|
status: 'ok',
|
|
98
26
|
});
|
|
99
27
|
return {
|
|
28
|
+
content: [{ type: 'text', text: JSON.stringify(result) }],
|
|
100
29
|
isError: false,
|
|
101
|
-
content: [
|
|
102
|
-
{
|
|
103
|
-
type: 'text',
|
|
104
|
-
text: JSON.stringify(result),
|
|
105
|
-
},
|
|
106
|
-
],
|
|
107
30
|
};
|
|
108
31
|
}
|
|
109
32
|
catch (e) {
|
|
110
|
-
|
|
33
|
+
const latency_ms = Date.now() - t0;
|
|
111
34
|
if (e instanceof OdooError) {
|
|
112
|
-
const
|
|
35
|
+
const formatted = formatMcpError(e);
|
|
113
36
|
logger.toolCall({
|
|
114
37
|
tool: 'odoo_execute',
|
|
115
|
-
args_sanitized: sanitizeArgs('odoo_execute',
|
|
38
|
+
args_sanitized: sanitizeArgs('odoo_execute', rawArgs),
|
|
116
39
|
latency_ms,
|
|
117
40
|
status: 'error',
|
|
118
|
-
error:
|
|
41
|
+
error: formatted.error_type,
|
|
119
42
|
});
|
|
120
43
|
return {
|
|
44
|
+
content: [{ type: 'text', text: JSON.stringify(formatted) }],
|
|
121
45
|
isError: true,
|
|
122
|
-
content: [
|
|
123
|
-
{
|
|
124
|
-
type: 'text',
|
|
125
|
-
text: JSON.stringify(formatMcpError(e)),
|
|
126
|
-
},
|
|
127
|
-
],
|
|
128
46
|
};
|
|
129
47
|
}
|
|
130
|
-
// Step 8: Non-OdooError — unexpected exception. Log + return as InternalError-shaped.
|
|
131
48
|
const message = e instanceof Error ? e.message : String(e);
|
|
132
|
-
const latency_ms = Date.now() - start;
|
|
133
49
|
logger.toolCall({
|
|
134
50
|
tool: 'odoo_execute',
|
|
135
|
-
args_sanitized: sanitizeArgs('odoo_execute',
|
|
51
|
+
args_sanitized: sanitizeArgs('odoo_execute', rawArgs),
|
|
136
52
|
latency_ms,
|
|
137
53
|
status: 'error',
|
|
138
54
|
error: 'InternalError',
|
|
139
55
|
});
|
|
140
56
|
return {
|
|
141
|
-
isError: true,
|
|
142
57
|
content: [
|
|
143
58
|
{
|
|
144
59
|
type: 'text',
|
|
@@ -149,6 +64,7 @@ export function registerExecuteTool(server, client, session, logger) {
|
|
|
149
64
|
}),
|
|
150
65
|
},
|
|
151
66
|
],
|
|
67
|
+
isError: true,
|
|
152
68
|
};
|
|
153
69
|
}
|
|
154
70
|
});
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"execute.js","sourceRoot":"","sources":["../../src/tools/execute.ts"],"names":[],"mappings":"AACA,OAAO,EAAgB,SAAS,EAAoB,YAAY,EAAE,MAAM,0BAA0B,CAAC;AAGnG,OAAO,EAAE,YAAY,EAAE,qBAAqB,EAAE,MAAM,eAAe,CAAC;AACpE,OAAO,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;AAE9C,OAAO,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AAE7C,
|
|
1
|
+
{"version":3,"file":"execute.js","sourceRoot":"","sources":["../../src/tools/execute.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"introspect.d.ts","sourceRoot":"","sources":["../../src/tools/introspect.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AACzE,OAAO,EAAa,KAAK,WAAW,EAAgB,MAAM,0BAA0B,CAAC;AACrF,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,0BAA0B,CAAC;AAI3D,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,cAAc,CAAC;
|
|
1
|
+
{"version":3,"file":"introspect.d.ts","sourceRoot":"","sources":["../../src/tools/introspect.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AACzE,OAAO,EAAa,KAAK,WAAW,EAAgB,MAAM,0BAA0B,CAAC;AACrF,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,0BAA0B,CAAC;AAI3D,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,cAAc,CAAC;AAG3C,wBAAgB,sBAAsB,CACpC,MAAM,EAAE,SAAS,EACjB,MAAM,EAAE,UAAU,EAClB,OAAO,EAAE,WAAW,EACpB,MAAM,EAAE,MAAM,GACb,IAAI,CAsEN"}
|
package/dist/tools/introspect.js
CHANGED
|
@@ -2,142 +2,58 @@ import { OdooError, sanitizeArgs } from '@netlinksinc/odoo-client';
|
|
|
2
2
|
import { buildContext, validateCompanySubset } from '../context.js';
|
|
3
3
|
import { formatMcpError } from '../errors.js';
|
|
4
4
|
import { fieldsGetSchema } from './schemas.js';
|
|
5
|
-
function inputValidationError(message) {
|
|
6
|
-
return {
|
|
7
|
-
isError: true,
|
|
8
|
-
content: [
|
|
9
|
-
{
|
|
10
|
-
type: 'text',
|
|
11
|
-
text: JSON.stringify({ error_type: 'InputValidationError', message }),
|
|
12
|
-
},
|
|
13
|
-
],
|
|
14
|
-
};
|
|
15
|
-
}
|
|
16
5
|
export function registerIntrospectTool(server, client, session, logger) {
|
|
17
|
-
server.
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
tool: 'odoo_fields_get',
|
|
24
|
-
args_sanitized: sanitizeArgs('odoo_fields_get', args),
|
|
25
|
-
latency_ms: Date.now() - start,
|
|
26
|
-
status: 'error',
|
|
27
|
-
error: 'InputValidationError',
|
|
28
|
-
});
|
|
29
|
-
return inputValidationError(parsed.error.message);
|
|
30
|
-
}
|
|
31
|
-
const data = parsed.data;
|
|
32
|
-
// Step 2: validate company subset if provided
|
|
33
|
-
if (data.allowed_company_ids !== undefined) {
|
|
34
|
-
try {
|
|
35
|
-
validateCompanySubset(data.allowed_company_ids, session.allowedCompanyIds);
|
|
36
|
-
}
|
|
37
|
-
catch (e) {
|
|
38
|
-
if (e instanceof OdooError) {
|
|
39
|
-
const latency_ms = Date.now() - start;
|
|
40
|
-
logger.toolCall({
|
|
41
|
-
tool: 'odoo_fields_get',
|
|
42
|
-
args_sanitized: sanitizeArgs('odoo_fields_get', args),
|
|
43
|
-
latency_ms,
|
|
44
|
-
status: 'error',
|
|
45
|
-
error: e.message,
|
|
46
|
-
});
|
|
47
|
-
return {
|
|
48
|
-
isError: true,
|
|
49
|
-
content: [
|
|
50
|
-
{
|
|
51
|
-
type: 'text',
|
|
52
|
-
text: JSON.stringify(formatMcpError(e)),
|
|
53
|
-
},
|
|
54
|
-
],
|
|
55
|
-
};
|
|
56
|
-
}
|
|
57
|
-
// Non-OdooError from company validation — log and return as InternalError.
|
|
58
|
-
const message = e instanceof Error ? e.message : String(e);
|
|
59
|
-
const latency_ms = Date.now() - start;
|
|
60
|
-
logger.toolCall({
|
|
61
|
-
tool: 'odoo_fields_get',
|
|
62
|
-
args_sanitized: sanitizeArgs('odoo_fields_get', args),
|
|
63
|
-
latency_ms,
|
|
64
|
-
status: 'error',
|
|
65
|
-
error: 'InternalError',
|
|
66
|
-
});
|
|
67
|
-
return {
|
|
68
|
-
isError: true,
|
|
69
|
-
content: [
|
|
70
|
-
{
|
|
71
|
-
type: 'text',
|
|
72
|
-
text: JSON.stringify({
|
|
73
|
-
error_type: 'InternalError',
|
|
74
|
-
message: 'unexpected error',
|
|
75
|
-
detail: message,
|
|
76
|
-
}),
|
|
77
|
-
},
|
|
78
|
-
],
|
|
79
|
-
};
|
|
80
|
-
}
|
|
81
|
-
}
|
|
82
|
-
// Step 3: build context (no extraContext for fields_get)
|
|
83
|
-
const context = buildContext(session, {
|
|
84
|
-
allowed_company_ids: data.allowed_company_ids,
|
|
85
|
-
active_company_id: data.active_company_id,
|
|
86
|
-
});
|
|
87
|
-
// Step 4: call client.fieldsGet — pass attributes as-is (undefined if not provided)
|
|
6
|
+
server.registerTool('odoo_fields_get', {
|
|
7
|
+
description: 'Introspect a model to discover its fields, types, labels, and constraints. Returns one entry per field.',
|
|
8
|
+
inputSchema: fieldsGetSchema.shape,
|
|
9
|
+
}, async (args) => {
|
|
10
|
+
const t0 = Date.now();
|
|
11
|
+
const rawArgs = args;
|
|
88
12
|
try {
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
13
|
+
if (args.allowed_company_ids) {
|
|
14
|
+
validateCompanySubset(args.allowed_company_ids, session.allowedCompanyIds);
|
|
15
|
+
}
|
|
16
|
+
const context = buildContext(session, {
|
|
17
|
+
allowed_company_ids: args.allowed_company_ids,
|
|
18
|
+
active_company_id: args.active_company_id,
|
|
19
|
+
});
|
|
20
|
+
const result = await client.fieldsGet(args.model, args.attributes, context);
|
|
92
21
|
logger.toolCall({
|
|
93
22
|
tool: 'odoo_fields_get',
|
|
94
|
-
args_sanitized: sanitizeArgs('odoo_fields_get',
|
|
95
|
-
latency_ms,
|
|
23
|
+
args_sanitized: sanitizeArgs('odoo_fields_get', rawArgs),
|
|
24
|
+
latency_ms: Date.now() - t0,
|
|
96
25
|
status: 'ok',
|
|
97
26
|
});
|
|
98
27
|
return {
|
|
28
|
+
content: [{ type: 'text', text: JSON.stringify(result) }],
|
|
99
29
|
isError: false,
|
|
100
|
-
content: [
|
|
101
|
-
{
|
|
102
|
-
type: 'text',
|
|
103
|
-
text: JSON.stringify(result),
|
|
104
|
-
},
|
|
105
|
-
],
|
|
106
30
|
};
|
|
107
31
|
}
|
|
108
32
|
catch (e) {
|
|
109
|
-
|
|
33
|
+
const latency_ms = Date.now() - t0;
|
|
110
34
|
if (e instanceof OdooError) {
|
|
111
|
-
const
|
|
35
|
+
const formatted = formatMcpError(e);
|
|
112
36
|
logger.toolCall({
|
|
113
37
|
tool: 'odoo_fields_get',
|
|
114
|
-
args_sanitized: sanitizeArgs('odoo_fields_get',
|
|
38
|
+
args_sanitized: sanitizeArgs('odoo_fields_get', rawArgs),
|
|
115
39
|
latency_ms,
|
|
116
40
|
status: 'error',
|
|
117
|
-
error:
|
|
41
|
+
error: formatted.error_type,
|
|
118
42
|
});
|
|
119
43
|
return {
|
|
44
|
+
content: [{ type: 'text', text: JSON.stringify(formatted) }],
|
|
120
45
|
isError: true,
|
|
121
|
-
content: [
|
|
122
|
-
{
|
|
123
|
-
type: 'text',
|
|
124
|
-
text: JSON.stringify(formatMcpError(e)),
|
|
125
|
-
},
|
|
126
|
-
],
|
|
127
46
|
};
|
|
128
47
|
}
|
|
129
|
-
// Step 7: Non-OdooError — unexpected exception. Log + return as InternalError-shaped.
|
|
130
48
|
const message = e instanceof Error ? e.message : String(e);
|
|
131
|
-
const latency_ms = Date.now() - start;
|
|
132
49
|
logger.toolCall({
|
|
133
50
|
tool: 'odoo_fields_get',
|
|
134
|
-
args_sanitized: sanitizeArgs('odoo_fields_get',
|
|
51
|
+
args_sanitized: sanitizeArgs('odoo_fields_get', rawArgs),
|
|
135
52
|
latency_ms,
|
|
136
53
|
status: 'error',
|
|
137
54
|
error: 'InternalError',
|
|
138
55
|
});
|
|
139
56
|
return {
|
|
140
|
-
isError: true,
|
|
141
57
|
content: [
|
|
142
58
|
{
|
|
143
59
|
type: 'text',
|
|
@@ -148,6 +64,7 @@ export function registerIntrospectTool(server, client, session, logger) {
|
|
|
148
64
|
}),
|
|
149
65
|
},
|
|
150
66
|
],
|
|
67
|
+
isError: true,
|
|
151
68
|
};
|
|
152
69
|
}
|
|
153
70
|
});
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"introspect.js","sourceRoot":"","sources":["../../src/tools/introspect.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,SAAS,EAAoB,YAAY,EAAE,MAAM,0BAA0B,CAAC;AAGrF,OAAO,EAAE,YAAY,EAAE,qBAAqB,EAAE,MAAM,eAAe,CAAC;AACpE,OAAO,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;AAE9C,OAAO,EAAE,eAAe,EAAE,MAAM,cAAc,CAAC;AAE/C,
|
|
1
|
+
{"version":3,"file":"introspect.js","sourceRoot":"","sources":["../../src/tools/introspect.ts"],"names":[],"mappings":"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"}
|
package/dist/tools/orm.d.ts
CHANGED
|
@@ -3,14 +3,12 @@ import { type OdooSession } from '@netlinksinc/odoo-client';
|
|
|
3
3
|
import type { OdooClient } from '@netlinksinc/odoo-client';
|
|
4
4
|
import type { Logger } from '../logger.js';
|
|
5
5
|
/**
|
|
6
|
-
* Register all 6 ORM tools on the MCP server
|
|
6
|
+
* Register all 6 ORM tools on the MCP server using the 3-arg registerTool
|
|
7
|
+
* overload that advertises the Zod input schema as JSON Schema in tools/list.
|
|
7
8
|
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
* The server.tool(name, cb) 2-arg overload is used deliberately so that
|
|
12
|
-
* validation failures return `isError: true` rather than a thrown McpError.
|
|
13
|
-
* Tests drive the handlers via a lightweight mock that stores the callback.
|
|
9
|
+
* Without inputSchema in the registration, MCP clients (Claude Code,
|
|
10
|
+
* Claude Desktop, etc.) see an empty properties bag and strip every arg
|
|
11
|
+
* before calling — making tools effectively unusable.
|
|
14
12
|
*/
|
|
15
13
|
export declare function registerOrmTools(server: McpServer, client: OdooClient, session: OdooSession, logger: Logger): void;
|
|
16
14
|
//# sourceMappingURL=orm.d.ts.map
|
package/dist/tools/orm.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"orm.d.ts","sourceRoot":"","sources":["../../src/tools/orm.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AACzE,OAAO,EAA2B,KAAK,WAAW,EAAgB,MAAM,0BAA0B,CAAC;AACnG,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,0BAA0B,CAAC;
|
|
1
|
+
{"version":3,"file":"orm.d.ts","sourceRoot":"","sources":["../../src/tools/orm.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AACzE,OAAO,EAA2B,KAAK,WAAW,EAAgB,MAAM,0BAA0B,CAAC;AACnG,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,0BAA0B,CAAC;AAI3D,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,cAAc,CAAC;AA+E3C;;;;;;;GAOG;AACH,wBAAgB,gBAAgB,CAC9B,MAAM,EAAE,SAAS,EACjB,MAAM,EAAE,UAAU,EAClB,OAAO,EAAE,WAAW,EACpB,MAAM,EAAE,MAAM,GACb,IAAI,CA6GN"}
|
package/dist/tools/orm.js
CHANGED
|
@@ -2,75 +2,46 @@ import { OdooError, sanitizeArgs } from '@netlinksinc/odoo-client';
|
|
|
2
2
|
import { buildContext, validateCompanySubset } from '../context.js';
|
|
3
3
|
import { formatMcpError } from '../errors.js';
|
|
4
4
|
import { createSchema, readSchema, searchCountSchema, searchReadSchema, unlinkSchema, writeSchema, } from './schemas.js';
|
|
5
|
-
/**
|
|
6
|
-
|
|
7
|
-
*
|
|
8
|
-
* Sequence:
|
|
9
|
-
* 1. Parse/validate args with the schema (returns isError on failure).
|
|
10
|
-
* 2. Validate allowed_company_ids against session (returns isError on failure).
|
|
11
|
-
* 3. Build Odoo RPC context.
|
|
12
|
-
* 4. Execute the client method.
|
|
13
|
-
* 5. Return serialised result or formatted OdooError.
|
|
14
|
-
*
|
|
15
|
-
* The callback is registered with the 2-arg overload `server.tool(name, cb)` so
|
|
16
|
-
* that tests can drive it via a simple mock that stores and directly calls `cb`.
|
|
17
|
-
* Inside the callback we perform our own safeParse so validation failures produce
|
|
18
|
-
* `isError: true` (not a thrown McpError as the SDK's schema-overload would do).
|
|
19
|
-
*/
|
|
20
|
-
async function executeHandler(tool, args, schema, exec, logger, session) {
|
|
5
|
+
/** Shape every tool handler funnels through after SDK-level Zod parsing. */
|
|
6
|
+
async function callOrm(tool, args, exec, logger, session) {
|
|
21
7
|
const t0 = Date.now();
|
|
8
|
+
const rawArgs = args;
|
|
22
9
|
try {
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
const text = JSON.stringify({
|
|
26
|
-
error_type: 'InputValidationError',
|
|
27
|
-
message: parsed.error.message,
|
|
28
|
-
});
|
|
29
|
-
logger.toolCall({
|
|
30
|
-
tool,
|
|
31
|
-
args_sanitized: sanitizeArgs(tool, args),
|
|
32
|
-
latency_ms: Date.now() - t0,
|
|
33
|
-
status: 'error',
|
|
34
|
-
error: 'InputValidationError',
|
|
35
|
-
});
|
|
36
|
-
return { content: [{ type: 'text', text }], isError: true };
|
|
37
|
-
}
|
|
38
|
-
// Company-subset enforcement (US-7 AC-7).
|
|
39
|
-
const data = parsed.data;
|
|
40
|
-
if (data.allowed_company_ids) {
|
|
41
|
-
validateCompanySubset(data.allowed_company_ids, session.allowedCompanyIds);
|
|
10
|
+
if (args.allowed_company_ids) {
|
|
11
|
+
validateCompanySubset(args.allowed_company_ids, session.allowedCompanyIds);
|
|
42
12
|
}
|
|
43
13
|
const context = buildContext(session, {
|
|
44
|
-
allowed_company_ids:
|
|
45
|
-
active_company_id:
|
|
14
|
+
allowed_company_ids: args.allowed_company_ids,
|
|
15
|
+
active_company_id: args.active_company_id,
|
|
46
16
|
});
|
|
47
|
-
const result = await exec(
|
|
17
|
+
const result = await exec(args, context);
|
|
48
18
|
logger.toolCall({
|
|
49
19
|
tool,
|
|
50
|
-
args_sanitized: sanitizeArgs(tool,
|
|
20
|
+
args_sanitized: sanitizeArgs(tool, rawArgs),
|
|
51
21
|
latency_ms: Date.now() - t0,
|
|
52
22
|
status: 'ok',
|
|
53
23
|
});
|
|
54
24
|
return { content: [{ type: 'text', text: JSON.stringify(result) }], isError: false };
|
|
55
25
|
}
|
|
56
26
|
catch (e) {
|
|
27
|
+
const latency_ms = Date.now() - t0;
|
|
57
28
|
if (e instanceof OdooError) {
|
|
58
29
|
const formatted = formatMcpError(e);
|
|
59
30
|
logger.toolCall({
|
|
60
31
|
tool,
|
|
61
|
-
args_sanitized: sanitizeArgs(tool,
|
|
62
|
-
latency_ms
|
|
32
|
+
args_sanitized: sanitizeArgs(tool, rawArgs),
|
|
33
|
+
latency_ms,
|
|
63
34
|
status: 'error',
|
|
64
35
|
error: formatted.error_type,
|
|
65
36
|
});
|
|
66
37
|
return { content: [{ type: 'text', text: JSON.stringify(formatted) }], isError: true };
|
|
67
38
|
}
|
|
68
|
-
// Non-OdooError —
|
|
39
|
+
// Non-OdooError — log + return InternalError shape (don't re-throw to MCP SDK)
|
|
69
40
|
const message = e instanceof Error ? e.message : String(e);
|
|
70
41
|
logger.toolCall({
|
|
71
42
|
tool,
|
|
72
|
-
args_sanitized: sanitizeArgs(tool,
|
|
73
|
-
latency_ms
|
|
43
|
+
args_sanitized: sanitizeArgs(tool, rawArgs),
|
|
44
|
+
latency_ms,
|
|
74
45
|
status: 'error',
|
|
75
46
|
error: 'InternalError',
|
|
76
47
|
});
|
|
@@ -90,50 +61,42 @@ async function executeHandler(tool, args, schema, exec, logger, session) {
|
|
|
90
61
|
}
|
|
91
62
|
}
|
|
92
63
|
/**
|
|
93
|
-
* Register all 6 ORM tools on the MCP server
|
|
94
|
-
*
|
|
95
|
-
* Tools registered: odoo_search_read, odoo_read, odoo_create, odoo_write,
|
|
96
|
-
* odoo_unlink, odoo_search_count.
|
|
64
|
+
* Register all 6 ORM tools on the MCP server using the 3-arg registerTool
|
|
65
|
+
* overload that advertises the Zod input schema as JSON Schema in tools/list.
|
|
97
66
|
*
|
|
98
|
-
*
|
|
99
|
-
*
|
|
100
|
-
*
|
|
67
|
+
* Without inputSchema in the registration, MCP clients (Claude Code,
|
|
68
|
+
* Claude Desktop, etc.) see an empty properties bag and strip every arg
|
|
69
|
+
* before calling — making tools effectively unusable.
|
|
101
70
|
*/
|
|
102
71
|
export function registerOrmTools(server, client, session, logger) {
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
server.tool('odoo_search_read', async (args) => executeHandler('odoo_search_read', args, searchReadSchema, (parsed, context) => client.searchRead(parsed.model, parsed.domain, parsed.fields, {
|
|
72
|
+
server.registerTool('odoo_search_read', {
|
|
73
|
+
description: 'Search and read records in one call. Returns matching rows up to `limit` (default 80).',
|
|
74
|
+
inputSchema: searchReadSchema.shape,
|
|
75
|
+
}, async (args) => callOrm('odoo_search_read', args, (parsed, context) => client.searchRead(parsed.model, parsed.domain, parsed.fields, {
|
|
108
76
|
limit: parsed.limit,
|
|
109
77
|
offset: parsed.offset,
|
|
110
78
|
order: parsed.order,
|
|
111
79
|
context,
|
|
112
80
|
}), logger, session));
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
server.
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
// -------------------------------------------------------------------------
|
|
134
|
-
// odoo_search_count
|
|
135
|
-
// -------------------------------------------------------------------------
|
|
136
|
-
// biome-ignore lint/suspicious/noExplicitAny: MCP SDK no-schema tool() overload signature mismatch
|
|
137
|
-
server.tool('odoo_search_count', async (args) => executeHandler('odoo_search_count', args, searchCountSchema, (parsed, context) => client.searchCount(parsed.model, parsed.domain, context), logger, session));
|
|
81
|
+
server.registerTool('odoo_read', {
|
|
82
|
+
description: 'Read specific record IDs from a model. Returns one row per ID.',
|
|
83
|
+
inputSchema: readSchema.shape,
|
|
84
|
+
}, async (args) => callOrm('odoo_read', args, (parsed, context) => client.read(parsed.model, parsed.ids, parsed.fields, context), logger, session));
|
|
85
|
+
server.registerTool('odoo_create', {
|
|
86
|
+
description: 'Create one or many records. `values` may be a single dict or an array of dicts.',
|
|
87
|
+
inputSchema: createSchema.shape,
|
|
88
|
+
}, async (args) => callOrm('odoo_create', args, (parsed, context) => client.create(parsed.model, parsed.values, context), logger, session));
|
|
89
|
+
server.registerTool('odoo_write', {
|
|
90
|
+
description: 'Update existing records. Applies `values` to every record in `ids`.',
|
|
91
|
+
inputSchema: writeSchema.shape,
|
|
92
|
+
}, async (args) => callOrm('odoo_write', args, (parsed, context) => client.write(parsed.model, parsed.ids, parsed.values, context), logger, session));
|
|
93
|
+
server.registerTool('odoo_unlink', {
|
|
94
|
+
description: 'Delete records. Returns true on success.',
|
|
95
|
+
inputSchema: unlinkSchema.shape,
|
|
96
|
+
}, async (args) => callOrm('odoo_unlink', args, (parsed, context) => client.unlink(parsed.model, parsed.ids, context), logger, session));
|
|
97
|
+
server.registerTool('odoo_search_count', {
|
|
98
|
+
description: 'Count records matching the domain. Returns an integer.',
|
|
99
|
+
inputSchema: searchCountSchema.shape,
|
|
100
|
+
}, async (args) => callOrm('odoo_search_count', args, (parsed, context) => client.searchCount(parsed.model, parsed.domain, context), logger, session));
|
|
138
101
|
}
|
|
139
102
|
//# sourceMappingURL=orm.js.map
|
package/dist/tools/orm.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"orm.js","sourceRoot":"","sources":["../../src/tools/orm.ts"],"names":[],"mappings":"AACA,OAAO,EAAgB,SAAS,EAAoB,YAAY,EAAE,MAAM,0BAA0B,CAAC;
|
|
1
|
+
{"version":3,"file":"orm.js","sourceRoot":"","sources":["../../src/tools/orm.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"report.d.ts","sourceRoot":"","sources":["../../src/tools/report.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AACzE,OAAO,EAA2B,KAAK,WAAW,EAAgB,MAAM,0BAA0B,CAAC;AACnG,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,0BAA0B,CAAC;AAI3D,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,cAAc,CAAC;AAG3C,wBAAgB,kBAAkB,CAChC,MAAM,EAAE,SAAS,EACjB,MAAM,EAAE,UAAU,EAClB,OAAO,EAAE,WAAW,EACpB,MAAM,EAAE,MAAM,GACb,IAAI,
|
|
1
|
+
{"version":3,"file":"report.d.ts","sourceRoot":"","sources":["../../src/tools/report.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AACzE,OAAO,EAA2B,KAAK,WAAW,EAAgB,MAAM,0BAA0B,CAAC;AACnG,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,0BAA0B,CAAC;AAI3D,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,cAAc,CAAC;AAG3C,wBAAgB,kBAAkB,CAChC,MAAM,EAAE,SAAS,EACjB,MAAM,EAAE,UAAU,EAClB,OAAO,EAAE,WAAW,EACpB,MAAM,EAAE,MAAM,GACb,IAAI,CA2EN"}
|
package/dist/tools/report.js
CHANGED
|
@@ -3,85 +3,58 @@ import { buildContext, validateCompanySubset } from '../context.js';
|
|
|
3
3
|
import { formatMcpError } from '../errors.js';
|
|
4
4
|
import { runReportSchema } from './schemas.js';
|
|
5
5
|
export function registerReportTool(server, client, session, logger) {
|
|
6
|
-
server.
|
|
6
|
+
server.registerTool('odoo_run_report', {
|
|
7
|
+
description: 'Render a QWeb PDF report for the given document IDs. Returns base64-encoded PDF content with its MIME type.',
|
|
8
|
+
inputSchema: runReportSchema.shape,
|
|
9
|
+
}, async (args) => {
|
|
7
10
|
const t0 = Date.now();
|
|
8
11
|
const toolName = 'odoo_run_report';
|
|
9
|
-
const
|
|
10
|
-
if (!parsed.success) {
|
|
11
|
-
const errorPayload = {
|
|
12
|
-
error_type: 'InputValidationError',
|
|
13
|
-
message: parsed.error.message,
|
|
14
|
-
};
|
|
15
|
-
logger.toolCall({
|
|
16
|
-
tool: toolName,
|
|
17
|
-
args_sanitized: sanitizeArgs(toolName, args),
|
|
18
|
-
latency_ms: Date.now() - t0,
|
|
19
|
-
status: 'error',
|
|
20
|
-
error: 'InputValidationError',
|
|
21
|
-
});
|
|
22
|
-
return {
|
|
23
|
-
isError: true,
|
|
24
|
-
content: [{ type: 'text', text: JSON.stringify(errorPayload) }],
|
|
25
|
-
};
|
|
26
|
-
}
|
|
27
|
-
const data = parsed.data;
|
|
12
|
+
const rawArgs = args;
|
|
28
13
|
try {
|
|
29
|
-
if (
|
|
30
|
-
validateCompanySubset(
|
|
14
|
+
if (args.allowed_company_ids) {
|
|
15
|
+
validateCompanySubset(args.allowed_company_ids, session.allowedCompanyIds);
|
|
31
16
|
}
|
|
32
17
|
const context = buildContext(session, {
|
|
33
|
-
allowed_company_ids:
|
|
34
|
-
active_company_id:
|
|
18
|
+
allowed_company_ids: args.allowed_company_ids,
|
|
19
|
+
active_company_id: args.active_company_id,
|
|
35
20
|
});
|
|
36
|
-
const { content, contentType } = await client.runReport(
|
|
37
|
-
const latency_ms = Date.now() - t0;
|
|
21
|
+
const { content, contentType } = await client.runReport(args.report_id, args.doc_ids, context);
|
|
38
22
|
logger.toolCall({
|
|
39
23
|
tool: toolName,
|
|
40
|
-
args_sanitized: sanitizeArgs(toolName,
|
|
41
|
-
latency_ms,
|
|
24
|
+
args_sanitized: sanitizeArgs(toolName, rawArgs),
|
|
25
|
+
latency_ms: Date.now() - t0,
|
|
42
26
|
status: 'ok',
|
|
43
27
|
});
|
|
44
28
|
return {
|
|
29
|
+
content: [{ type: 'text', text: JSON.stringify({ content, contentType }) }],
|
|
45
30
|
isError: false,
|
|
46
|
-
content: [
|
|
47
|
-
{
|
|
48
|
-
type: 'text',
|
|
49
|
-
text: JSON.stringify({ content, contentType }),
|
|
50
|
-
},
|
|
51
|
-
],
|
|
52
31
|
};
|
|
53
32
|
}
|
|
54
33
|
catch (e) {
|
|
55
34
|
const latency_ms = Date.now() - t0;
|
|
56
35
|
if (e instanceof OdooError) {
|
|
36
|
+
const formatted = formatMcpError(e);
|
|
57
37
|
logger.toolCall({
|
|
58
38
|
tool: toolName,
|
|
59
|
-
args_sanitized: sanitizeArgs(toolName,
|
|
39
|
+
args_sanitized: sanitizeArgs(toolName, rawArgs),
|
|
60
40
|
latency_ms,
|
|
61
41
|
status: 'error',
|
|
62
|
-
error:
|
|
42
|
+
error: formatted.error_type,
|
|
63
43
|
});
|
|
64
44
|
return {
|
|
45
|
+
content: [{ type: 'text', text: JSON.stringify(formatted) }],
|
|
65
46
|
isError: true,
|
|
66
|
-
content: [
|
|
67
|
-
{
|
|
68
|
-
type: 'text',
|
|
69
|
-
text: JSON.stringify(formatMcpError(e)),
|
|
70
|
-
},
|
|
71
|
-
],
|
|
72
47
|
};
|
|
73
48
|
}
|
|
74
|
-
// Non-OdooError — unexpected exception. Log + return as InternalError-shaped.
|
|
75
49
|
const message = e instanceof Error ? e.message : String(e);
|
|
76
50
|
logger.toolCall({
|
|
77
51
|
tool: toolName,
|
|
78
|
-
args_sanitized: sanitizeArgs(toolName,
|
|
52
|
+
args_sanitized: sanitizeArgs(toolName, rawArgs),
|
|
79
53
|
latency_ms,
|
|
80
54
|
status: 'error',
|
|
81
55
|
error: 'InternalError',
|
|
82
56
|
});
|
|
83
57
|
return {
|
|
84
|
-
isError: true,
|
|
85
58
|
content: [
|
|
86
59
|
{
|
|
87
60
|
type: 'text',
|
|
@@ -92,6 +65,7 @@ export function registerReportTool(server, client, session, logger) {
|
|
|
92
65
|
}),
|
|
93
66
|
},
|
|
94
67
|
],
|
|
68
|
+
isError: true,
|
|
95
69
|
};
|
|
96
70
|
}
|
|
97
71
|
});
|
package/dist/tools/report.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"report.js","sourceRoot":"","sources":["../../src/tools/report.ts"],"names":[],"mappings":"AACA,OAAO,EAAgB,SAAS,EAAoB,YAAY,EAAE,MAAM,0BAA0B,CAAC;AAGnG,OAAO,EAAE,YAAY,EAAE,qBAAqB,EAAE,MAAM,eAAe,CAAC;AACpE,OAAO,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;AAE9C,OAAO,EAAE,eAAe,EAAE,MAAM,cAAc,CAAC;AAE/C,MAAM,UAAU,kBAAkB,CAChC,MAAiB,EACjB,MAAkB,EAClB,OAAoB,EACpB,MAAc;IAEd,MAAM,CAAC,
|
|
1
|
+
{"version":3,"file":"report.js","sourceRoot":"","sources":["../../src/tools/report.ts"],"names":[],"mappings":"AACA,OAAO,EAAgB,SAAS,EAAoB,YAAY,EAAE,MAAM,0BAA0B,CAAC;AAGnG,OAAO,EAAE,YAAY,EAAE,qBAAqB,EAAE,MAAM,eAAe,CAAC;AACpE,OAAO,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;AAE9C,OAAO,EAAE,eAAe,EAAE,MAAM,cAAc,CAAC;AAE/C,MAAM,UAAU,kBAAkB,CAChC,MAAiB,EACjB,MAAkB,EAClB,OAAoB,EACpB,MAAc;IAEd,MAAM,CAAC,YAAY,CACjB,iBAAiB,EACjB;QACE,WAAW,EACT,6GAA6G;QAC/G,WAAW,EAAE,eAAe,CAAC,KAAK;KACnC,EACD,KAAK,EAAE,IAAI,EAAE,EAAE;QACb,MAAM,EAAE,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;QACtB,MAAM,QAAQ,GAAG,iBAAiB,CAAC;QACnC,MAAM,OAAO,GAAG,IAA0C,CAAC;QAC3D,IAAI,CAAC;YACH,IAAI,IAAI,CAAC,mBAAmB,EAAE,CAAC;gBAC7B,qBAAqB,CAAC,IAAI,CAAC,mBAAmB,EAAE,OAAO,CAAC,iBAAiB,CAAC,CAAC;YAC7E,CAAC;YACD,MAAM,OAAO,GAAY,YAAY,CAAC,OAAO,EAAE;gBAC7C,mBAAmB,EAAE,IAAI,CAAC,mBAAmB;gBAC7C,iBAAiB,EAAE,IAAI,CAAC,iBAAiB;aAC1C,CAAC,CAAC;YACH,MAAM,EAAE,OAAO,EAAE,WAAW,EAAE,GAAG,MAAM,MAAM,CAAC,SAAS,CACrD,IAAI,CAAC,SAAS,EACd,IAAI,CAAC,OAAO,EACZ,OAAO,CACR,CAAC;YACF,MAAM,CAAC,QAAQ,CAAC;gBACd,IAAI,EAAE,QAAQ;gBACd,cAAc,EAAE,YAAY,CAAC,QAAQ,EAAE,OAAO,CAAC;gBAC/C,UAAU,EAAE,IAAI,CAAC,GAAG,EAAE,GAAG,EAAE;gBAC3B,MAAM,EAAE,IAAI;aACb,CAAC,CAAC;YACH,OAAO;gBACL,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAe,EAAE,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,EAAE,OAAO,EAAE,WAAW,EAAE,CAAC,EAAE,CAAC;gBACpF,OAAO,EAAE,KAAK;aACf,CAAC;QACJ,CAAC;QAAC,OAAO,CAAC,EAAE,CAAC;YACX,MAAM,UAAU,GAAG,IAAI,CAAC,GAAG,EAAE,GAAG,EAAE,CAAC;YACnC,IAAI,CAAC,YAAY,SAAS,EAAE,CAAC;gBAC3B,MAAM,SAAS,GAAG,cAAc,CAAC,CAAC,CAAC,CAAC;gBACpC,MAAM,CAAC,QAAQ,CAAC;oBACd,IAAI,EAAE,QAAQ;oBACd,cAAc,EAAE,YAAY,CAAC,QAAQ,EAAE,OAAO,CAAC;oBAC/C,UAAU;oBACV,MAAM,EAAE,OAAO;oBACf,KAAK,EAAE,SAAS,CAAC,UAAU;iBAC5B,CAAC,CAAC;gBACH,OAAO;oBACL,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAe,EAAE,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,SAAS,CAAC,EAAE,CAAC;oBACrE,OAAO,EAAE,IAAI;iBACd,CAAC;YACJ,CAAC;YACD,MAAM,OAAO,GAAG,CAAC,YAAY,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC;YAC3D,MAAM,CAAC,QAAQ,CAAC;gBACd,IAAI,EAAE,QAAQ;gBACd,cAAc,EAAE,YAAY,CAAC,QAAQ,EAAE,OAAO,CAAC;gBAC/C,UAAU;gBACV,MAAM,EAAE,OAAO;gBACf,KAAK,EAAE,eAAe;aACvB,CAAC,CAAC;YACH,OAAO;gBACL,OAAO,EAAE;oBACP;wBACE,IAAI,EAAE,MAAe;wBACrB,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC;4BACnB,UAAU,EAAE,eAAe;4BAC3B,OAAO,EAAE,kBAAkB;4BAC3B,MAAM,EAAE,OAAO;yBAChB,CAAC;qBACH;iBACF;gBACD,OAAO,EAAE,IAAI;aACd,CAAC;QACJ,CAAC;IACH,CAAC,CACF,CAAC;AACJ,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@netlinksinc/odoo-mcp",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.1",
|
|
4
4
|
"description": "MCP server exposing any Odoo 19 instance to Claude (Desktop, Code, Cowork) — generic-only tool surface, works on stock and customized Odoo",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"mcp",
|
|
@@ -43,7 +43,7 @@
|
|
|
43
43
|
"dependencies": {
|
|
44
44
|
"@modelcontextprotocol/sdk": "^1",
|
|
45
45
|
"zod": "^3",
|
|
46
|
-
"@netlinksinc/odoo-client": "0.1.
|
|
46
|
+
"@netlinksinc/odoo-client": "0.1.1"
|
|
47
47
|
},
|
|
48
48
|
"devDependencies": {
|
|
49
49
|
"typescript": "^5.7",
|