@netlinksinc/odoo-mcp 0.1.0
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/LICENSE +21 -0
- package/dist/bin.d.ts +3 -0
- package/dist/bin.d.ts.map +1 -0
- package/dist/bin.js +37 -0
- package/dist/bin.js.map +1 -0
- package/dist/config.d.ts +7 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/config.js +59 -0
- package/dist/config.js.map +1 -0
- package/dist/context.d.ts +16 -0
- package/dist/context.d.ts.map +1 -0
- package/dist/context.js +37 -0
- package/dist/context.js.map +1 -0
- package/dist/errors.d.ts +10 -0
- package/dist/errors.d.ts.map +1 -0
- package/dist/errors.js +15 -0
- package/dist/errors.js.map +1 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +2 -0
- package/dist/index.js.map +1 -0
- package/dist/logger.d.ts +26 -0
- package/dist/logger.d.ts.map +1 -0
- package/dist/logger.js +57 -0
- package/dist/logger.js.map +1 -0
- package/dist/probe.d.ts +19 -0
- package/dist/probe.d.ts.map +1 -0
- package/dist/probe.js +181 -0
- package/dist/probe.js.map +1 -0
- package/dist/resources.d.ts +11 -0
- package/dist/resources.d.ts.map +1 -0
- package/dist/resources.js +37 -0
- package/dist/resources.js.map +1 -0
- package/dist/server.d.ts +20 -0
- package/dist/server.d.ts.map +1 -0
- package/dist/server.js +32 -0
- package/dist/server.js.map +1 -0
- package/dist/tools/action.d.ts +6 -0
- package/dist/tools/action.d.ts.map +1 -0
- package/dist/tools/action.js +158 -0
- package/dist/tools/action.js.map +1 -0
- package/dist/tools/execute.d.ts +6 -0
- package/dist/tools/execute.d.ts.map +1 -0
- package/dist/tools/execute.js +156 -0
- package/dist/tools/execute.js.map +1 -0
- package/dist/tools/index.d.ts +5 -0
- package/dist/tools/index.d.ts.map +1 -0
- package/dist/tools/index.js +13 -0
- package/dist/tools/index.js.map +1 -0
- package/dist/tools/introspect.d.ts +6 -0
- package/dist/tools/introspect.d.ts.map +1 -0
- package/dist/tools/introspect.js +155 -0
- package/dist/tools/introspect.js.map +1 -0
- package/dist/tools/orm.d.ts +16 -0
- package/dist/tools/orm.d.ts.map +1 -0
- package/dist/tools/orm.js +139 -0
- package/dist/tools/orm.js.map +1 -0
- package/dist/tools/report.d.ts +6 -0
- package/dist/tools/report.d.ts.map +1 -0
- package/dist/tools/report.js +99 -0
- package/dist/tools/report.js.map +1 -0
- package/dist/tools/schemas.d.ts +202 -0
- package/dist/tools/schemas.d.ts.map +1 -0
- package/dist/tools/schemas.js +77 -0
- package/dist/tools/schemas.js.map +1 -0
- package/package.json +56 -0
package/dist/server.d.ts
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
|
|
2
|
+
import { type OdooConfig } from '@netlinksinc/odoo-client';
|
|
3
|
+
import { type Logger } from './logger.js';
|
|
4
|
+
export interface McpServerConfig {
|
|
5
|
+
odooConfig: OdooConfig;
|
|
6
|
+
logFile?: string;
|
|
7
|
+
}
|
|
8
|
+
/**
|
|
9
|
+
* Wires all subsystems together and returns the ready-to-connect MCP server
|
|
10
|
+
* along with the logger instance (so bin.ts can call logger.startup and
|
|
11
|
+
* logger.shutdown without opening a second file descriptor on the log file).
|
|
12
|
+
*
|
|
13
|
+
* Authentication errors (OdooAuthError) are intentionally NOT caught here —
|
|
14
|
+
* they propagate to the caller (bin.ts), which serialises them to stderr.
|
|
15
|
+
*/
|
|
16
|
+
export declare function createOdooMcpServer(config: McpServerConfig): Promise<{
|
|
17
|
+
server: McpServer;
|
|
18
|
+
logger: Logger;
|
|
19
|
+
}>;
|
|
20
|
+
//# sourceMappingURL=server.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AACpE,OAAO,EAAc,KAAK,UAAU,EAAE,MAAM,0BAA0B,CAAC;AAEvE,OAAO,EAAE,KAAK,MAAM,EAAgB,MAAM,aAAa,CAAC;AAKxD,MAAM,WAAW,eAAe;IAC9B,UAAU,EAAE,UAAU,CAAC;IACvB,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED;;;;;;;GAOG;AACH,wBAAsB,mBAAmB,CACvC,MAAM,EAAE,eAAe,GACtB,OAAO,CAAC;IAAE,MAAM,EAAE,SAAS,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CAAC,CAuBhD"}
|
package/dist/server.js
ADDED
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
|
|
2
|
+
import { OdooClient } from '@netlinksinc/odoo-client';
|
|
3
|
+
import { createLogger } from './logger.js';
|
|
4
|
+
import { runProbe } from './probe.js';
|
|
5
|
+
import { registerResources } from './resources.js';
|
|
6
|
+
import { registerAllTools } from './tools/index.js';
|
|
7
|
+
/**
|
|
8
|
+
* Wires all subsystems together and returns the ready-to-connect MCP server
|
|
9
|
+
* along with the logger instance (so bin.ts can call logger.startup and
|
|
10
|
+
* logger.shutdown without opening a second file descriptor on the log file).
|
|
11
|
+
*
|
|
12
|
+
* Authentication errors (OdooAuthError) are intentionally NOT caught here —
|
|
13
|
+
* they propagate to the caller (bin.ts), which serialises them to stderr.
|
|
14
|
+
*/
|
|
15
|
+
export async function createOdooMcpServer(config) {
|
|
16
|
+
// 1. Build the Odoo client.
|
|
17
|
+
const client = new OdooClient(config.odooConfig);
|
|
18
|
+
// 2. Authenticate — may throw OdooAuthError; propagate to caller (US-2 AC-3).
|
|
19
|
+
const session = await client.authenticate();
|
|
20
|
+
// 3. Create logger (opens the log-file fd once).
|
|
21
|
+
const logger = createLogger(config.logFile);
|
|
22
|
+
// 4. Run the startup probe (never throws — always resolves).
|
|
23
|
+
const probe = await runProbe(client);
|
|
24
|
+
// 5. Create the MCP server.
|
|
25
|
+
const server = new McpServer({ name: 'odoo-mcp', version: '0.1.0' });
|
|
26
|
+
// 6. Register resources (static, backed by probe snapshot).
|
|
27
|
+
registerResources(server, probe);
|
|
28
|
+
// 7. Register all tool handlers.
|
|
29
|
+
registerAllTools(server, client, session, logger);
|
|
30
|
+
return { server, logger };
|
|
31
|
+
}
|
|
32
|
+
//# sourceMappingURL=server.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"server.js","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AACpE,OAAO,EAAE,UAAU,EAAmB,MAAM,0BAA0B,CAAC;AAEvE,OAAO,EAAe,YAAY,EAAE,MAAM,aAAa,CAAC;AACxD,OAAO,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AACtC,OAAO,EAAE,iBAAiB,EAAE,MAAM,gBAAgB,CAAC;AACnD,OAAO,EAAE,gBAAgB,EAAE,MAAM,kBAAkB,CAAC;AAOpD;;;;;;;GAOG;AACH,MAAM,CAAC,KAAK,UAAU,mBAAmB,CACvC,MAAuB;IAEvB,4BAA4B;IAC5B,MAAM,MAAM,GAAG,IAAI,UAAU,CAAC,MAAM,CAAC,UAAU,CAAC,CAAC;IAEjD,8EAA8E;IAC9E,MAAM,OAAO,GAAG,MAAM,MAAM,CAAC,YAAY,EAAE,CAAC;IAE5C,iDAAiD;IACjD,MAAM,MAAM,GAAG,YAAY,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC;IAE5C,6DAA6D;IAC7D,MAAM,KAAK,GAAG,MAAM,QAAQ,CAAC,MAAM,CAAC,CAAC;IAErC,4BAA4B;IAC5B,MAAM,MAAM,GAAG,IAAI,SAAS,CAAC,EAAE,IAAI,EAAE,UAAU,EAAE,OAAO,EAAE,OAAO,EAAE,CAAC,CAAC;IAErE,4DAA4D;IAC5D,iBAAiB,CAAC,MAAM,EAAE,KAAK,CAAC,CAAC;IAEjC,iCAAiC;IACjC,gBAAgB,CAAC,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,CAAC,CAAC;IAElD,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,CAAC;AAC5B,CAAC"}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
|
|
2
|
+
import { type OdooSession } from '@netlinksinc/odoo-client';
|
|
3
|
+
import type { OdooClient } from '@netlinksinc/odoo-client';
|
|
4
|
+
import type { Logger } from '../logger.js';
|
|
5
|
+
export declare function registerActionTool(server: McpServer, client: OdooClient, session: OdooSession, logger: Logger): void;
|
|
6
|
+
//# sourceMappingURL=action.d.ts.map
|
|
@@ -0,0 +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;AAe3C,wBAAgB,kBAAkB,CAChC,MAAM,EAAE,SAAS,EACjB,MAAM,EAAE,UAAU,EAClB,OAAO,EAAE,WAAW,EACpB,MAAM,EAAE,MAAM,GACb,IAAI,CAuJN"}
|
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
import { OdooError, sanitizeArgs } from '@netlinksinc/odoo-client';
|
|
2
|
+
import { buildContext, validateCompanySubset } from '../context.js';
|
|
3
|
+
import { formatMcpError } from '../errors.js';
|
|
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
|
+
export function registerActionTool(server, client, session, logger) {
|
|
17
|
+
// Use the no-schema overload so we can return InputValidationError manually.
|
|
18
|
+
server.tool('odoo_call_action', async (args) => {
|
|
19
|
+
const start = Date.now();
|
|
20
|
+
// Step 1: parse with Zod schema (model + action_name regex enforced via MODEL_NAME/METHOD_NAME)
|
|
21
|
+
const parsed = callActionSchema.safeParse(args);
|
|
22
|
+
if (!parsed.success) {
|
|
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
|
|
91
|
+
try {
|
|
92
|
+
const result = await client.callAction(data.model, data.ids, data.action_name, context);
|
|
93
|
+
const latency_ms = Date.now() - start;
|
|
94
|
+
// Step 5: return result and log
|
|
95
|
+
logger.toolCall({
|
|
96
|
+
tool: 'odoo_call_action',
|
|
97
|
+
args_sanitized: sanitizeArgs('odoo_call_action', args),
|
|
98
|
+
latency_ms,
|
|
99
|
+
status: 'ok',
|
|
100
|
+
});
|
|
101
|
+
return {
|
|
102
|
+
isError: false,
|
|
103
|
+
content: [
|
|
104
|
+
{
|
|
105
|
+
type: 'text',
|
|
106
|
+
text: JSON.stringify(result),
|
|
107
|
+
},
|
|
108
|
+
],
|
|
109
|
+
};
|
|
110
|
+
}
|
|
111
|
+
catch (e) {
|
|
112
|
+
// Step 6: on OdooError, format and return isError:true
|
|
113
|
+
if (e instanceof OdooError) {
|
|
114
|
+
const latency_ms = Date.now() - start;
|
|
115
|
+
logger.toolCall({
|
|
116
|
+
tool: 'odoo_call_action',
|
|
117
|
+
args_sanitized: sanitizeArgs('odoo_call_action', args),
|
|
118
|
+
latency_ms,
|
|
119
|
+
status: 'error',
|
|
120
|
+
error: e.message,
|
|
121
|
+
});
|
|
122
|
+
return {
|
|
123
|
+
isError: true,
|
|
124
|
+
content: [
|
|
125
|
+
{
|
|
126
|
+
type: 'text',
|
|
127
|
+
text: JSON.stringify(formatMcpError(e)),
|
|
128
|
+
},
|
|
129
|
+
],
|
|
130
|
+
};
|
|
131
|
+
}
|
|
132
|
+
// Step 7: Non-OdooError — unexpected exception. Log + return as InternalError-shaped.
|
|
133
|
+
const message = e instanceof Error ? e.message : String(e);
|
|
134
|
+
const latency_ms = Date.now() - start;
|
|
135
|
+
logger.toolCall({
|
|
136
|
+
tool: 'odoo_call_action',
|
|
137
|
+
args_sanitized: sanitizeArgs('odoo_call_action', args),
|
|
138
|
+
latency_ms,
|
|
139
|
+
status: 'error',
|
|
140
|
+
error: 'InternalError',
|
|
141
|
+
});
|
|
142
|
+
return {
|
|
143
|
+
isError: true,
|
|
144
|
+
content: [
|
|
145
|
+
{
|
|
146
|
+
type: 'text',
|
|
147
|
+
text: JSON.stringify({
|
|
148
|
+
error_type: 'InternalError',
|
|
149
|
+
message: 'unexpected error',
|
|
150
|
+
detail: message,
|
|
151
|
+
}),
|
|
152
|
+
},
|
|
153
|
+
],
|
|
154
|
+
};
|
|
155
|
+
}
|
|
156
|
+
});
|
|
157
|
+
}
|
|
158
|
+
//# sourceMappingURL=action.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"action.js","sourceRoot":"","sources":["../../src/tools/action.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
|
|
2
|
+
import { type OdooSession } from '@netlinksinc/odoo-client';
|
|
3
|
+
import type { OdooClient } from '@netlinksinc/odoo-client';
|
|
4
|
+
import type { Logger } from '../logger.js';
|
|
5
|
+
export declare function registerExecuteTool(server: McpServer, client: OdooClient, session: OdooSession, logger: Logger): void;
|
|
6
|
+
//# sourceMappingURL=execute.d.ts.map
|
|
@@ -0,0 +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;AAe3C,wBAAgB,mBAAmB,CACjC,MAAM,EAAE,SAAS,EACjB,MAAM,EAAE,UAAU,EAClB,OAAO,EAAE,WAAW,EACpB,MAAM,EAAE,MAAM,GACb,IAAI,CAiJN"}
|
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
import { OdooError, sanitizeArgs } from '@netlinksinc/odoo-client';
|
|
2
|
+
import { buildContext, validateCompanySubset } from '../context.js';
|
|
3
|
+
import { formatMcpError } from '../errors.js';
|
|
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
|
+
export function registerExecuteTool(server, client, session, logger) {
|
|
17
|
+
// Use the no-schema overload so we can return InputValidationError manually.
|
|
18
|
+
server.tool('odoo_execute', async (args) => {
|
|
19
|
+
const start = Date.now();
|
|
20
|
+
// Step 1: parse with Zod schema (includes model + method regex via MODEL_NAME/METHOD_NAME)
|
|
21
|
+
const parsed = executeSchema.safeParse(args);
|
|
22
|
+
if (!parsed.success) {
|
|
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
|
|
89
|
+
try {
|
|
90
|
+
const result = await client.execute(data.model, data.method, data.args, data.kwargs, context);
|
|
91
|
+
const latency_ms = Date.now() - start;
|
|
92
|
+
// Step 5 & 6: return result and log
|
|
93
|
+
logger.toolCall({
|
|
94
|
+
tool: 'odoo_execute',
|
|
95
|
+
args_sanitized: sanitizeArgs('odoo_execute', args),
|
|
96
|
+
latency_ms,
|
|
97
|
+
status: 'ok',
|
|
98
|
+
});
|
|
99
|
+
return {
|
|
100
|
+
isError: false,
|
|
101
|
+
content: [
|
|
102
|
+
{
|
|
103
|
+
type: 'text',
|
|
104
|
+
text: JSON.stringify(result),
|
|
105
|
+
},
|
|
106
|
+
],
|
|
107
|
+
};
|
|
108
|
+
}
|
|
109
|
+
catch (e) {
|
|
110
|
+
// Step 7: on OdooError, format and return isError:true
|
|
111
|
+
if (e instanceof OdooError) {
|
|
112
|
+
const latency_ms = Date.now() - start;
|
|
113
|
+
logger.toolCall({
|
|
114
|
+
tool: 'odoo_execute',
|
|
115
|
+
args_sanitized: sanitizeArgs('odoo_execute', args),
|
|
116
|
+
latency_ms,
|
|
117
|
+
status: 'error',
|
|
118
|
+
error: e.message,
|
|
119
|
+
});
|
|
120
|
+
return {
|
|
121
|
+
isError: true,
|
|
122
|
+
content: [
|
|
123
|
+
{
|
|
124
|
+
type: 'text',
|
|
125
|
+
text: JSON.stringify(formatMcpError(e)),
|
|
126
|
+
},
|
|
127
|
+
],
|
|
128
|
+
};
|
|
129
|
+
}
|
|
130
|
+
// Step 8: Non-OdooError — unexpected exception. Log + return as InternalError-shaped.
|
|
131
|
+
const message = e instanceof Error ? e.message : String(e);
|
|
132
|
+
const latency_ms = Date.now() - start;
|
|
133
|
+
logger.toolCall({
|
|
134
|
+
tool: 'odoo_execute',
|
|
135
|
+
args_sanitized: sanitizeArgs('odoo_execute', args),
|
|
136
|
+
latency_ms,
|
|
137
|
+
status: 'error',
|
|
138
|
+
error: 'InternalError',
|
|
139
|
+
});
|
|
140
|
+
return {
|
|
141
|
+
isError: true,
|
|
142
|
+
content: [
|
|
143
|
+
{
|
|
144
|
+
type: 'text',
|
|
145
|
+
text: JSON.stringify({
|
|
146
|
+
error_type: 'InternalError',
|
|
147
|
+
message: 'unexpected error',
|
|
148
|
+
detail: message,
|
|
149
|
+
}),
|
|
150
|
+
},
|
|
151
|
+
],
|
|
152
|
+
};
|
|
153
|
+
}
|
|
154
|
+
});
|
|
155
|
+
}
|
|
156
|
+
//# sourceMappingURL=execute.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"execute.js","sourceRoot":"","sources":["../../src/tools/execute.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
|
|
2
|
+
import type { OdooClient, OdooSession } from '@netlinksinc/odoo-client';
|
|
3
|
+
import type { Logger } from '../logger.js';
|
|
4
|
+
export declare function registerAllTools(server: McpServer, client: OdooClient, session: OdooSession, logger: Logger): void;
|
|
5
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/tools/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AACzE,OAAO,KAAK,EAAE,UAAU,EAAE,WAAW,EAAE,MAAM,0BAA0B,CAAC;AAExE,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,cAAc,CAAC;AAO3C,wBAAgB,gBAAgB,CAC9B,MAAM,EAAE,SAAS,EACjB,MAAM,EAAE,UAAU,EAClB,OAAO,EAAE,WAAW,EACpB,MAAM,EAAE,MAAM,GACb,IAAI,CAMN"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { registerActionTool } from './action.js';
|
|
2
|
+
import { registerExecuteTool } from './execute.js';
|
|
3
|
+
import { registerIntrospectTool } from './introspect.js';
|
|
4
|
+
import { registerOrmTools } from './orm.js';
|
|
5
|
+
import { registerReportTool } from './report.js';
|
|
6
|
+
export function registerAllTools(server, client, session, logger) {
|
|
7
|
+
registerOrmTools(server, client, session, logger);
|
|
8
|
+
registerExecuteTool(server, client, session, logger);
|
|
9
|
+
registerReportTool(server, client, session, logger);
|
|
10
|
+
registerActionTool(server, client, session, logger);
|
|
11
|
+
registerIntrospectTool(server, client, session, logger);
|
|
12
|
+
}
|
|
13
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/tools/index.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,kBAAkB,EAAE,MAAM,aAAa,CAAC;AACjD,OAAO,EAAE,mBAAmB,EAAE,MAAM,cAAc,CAAC;AACnD,OAAO,EAAE,sBAAsB,EAAE,MAAM,iBAAiB,CAAC;AACzD,OAAO,EAAE,gBAAgB,EAAE,MAAM,UAAU,CAAC;AAC5C,OAAO,EAAE,kBAAkB,EAAE,MAAM,aAAa,CAAC;AAEjD,MAAM,UAAU,gBAAgB,CAC9B,MAAiB,EACjB,MAAkB,EAClB,OAAoB,EACpB,MAAc;IAEd,gBAAgB,CAAC,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,CAAC,CAAC;IAClD,mBAAmB,CAAC,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,CAAC,CAAC;IACrD,kBAAkB,CAAC,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,CAAC,CAAC;IACpD,kBAAkB,CAAC,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,CAAC,CAAC;IACpD,sBAAsB,CAAC,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,CAAC,CAAC;AAC1D,CAAC"}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
|
|
2
|
+
import { type OdooSession } from '@netlinksinc/odoo-client';
|
|
3
|
+
import type { OdooClient } from '@netlinksinc/odoo-client';
|
|
4
|
+
import type { Logger } from '../logger.js';
|
|
5
|
+
export declare function registerIntrospectTool(server: McpServer, client: OdooClient, session: OdooSession, logger: Logger): void;
|
|
6
|
+
//# sourceMappingURL=introspect.d.ts.map
|
|
@@ -0,0 +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;AAe3C,wBAAgB,sBAAsB,CACpC,MAAM,EAAE,SAAS,EACjB,MAAM,EAAE,UAAU,EAClB,OAAO,EAAE,WAAW,EACpB,MAAM,EAAE,MAAM,GACb,IAAI,CAgJN"}
|
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
import { OdooError, sanitizeArgs } from '@netlinksinc/odoo-client';
|
|
2
|
+
import { buildContext, validateCompanySubset } from '../context.js';
|
|
3
|
+
import { formatMcpError } from '../errors.js';
|
|
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
|
+
export function registerIntrospectTool(server, client, session, logger) {
|
|
17
|
+
server.tool('odoo_fields_get', async (args) => {
|
|
18
|
+
const start = Date.now();
|
|
19
|
+
// Step 1: parse with Zod schema (model regex enforced via MODEL_NAME)
|
|
20
|
+
const parsed = fieldsGetSchema.safeParse(args);
|
|
21
|
+
if (!parsed.success) {
|
|
22
|
+
logger.toolCall({
|
|
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)
|
|
88
|
+
try {
|
|
89
|
+
const result = await client.fieldsGet(data.model, data.attributes, context);
|
|
90
|
+
const latency_ms = Date.now() - start;
|
|
91
|
+
// Step 5: return result and log
|
|
92
|
+
logger.toolCall({
|
|
93
|
+
tool: 'odoo_fields_get',
|
|
94
|
+
args_sanitized: sanitizeArgs('odoo_fields_get', args),
|
|
95
|
+
latency_ms,
|
|
96
|
+
status: 'ok',
|
|
97
|
+
});
|
|
98
|
+
return {
|
|
99
|
+
isError: false,
|
|
100
|
+
content: [
|
|
101
|
+
{
|
|
102
|
+
type: 'text',
|
|
103
|
+
text: JSON.stringify(result),
|
|
104
|
+
},
|
|
105
|
+
],
|
|
106
|
+
};
|
|
107
|
+
}
|
|
108
|
+
catch (e) {
|
|
109
|
+
// Step 6: on OdooError, format and return isError:true
|
|
110
|
+
if (e instanceof OdooError) {
|
|
111
|
+
const latency_ms = Date.now() - start;
|
|
112
|
+
logger.toolCall({
|
|
113
|
+
tool: 'odoo_fields_get',
|
|
114
|
+
args_sanitized: sanitizeArgs('odoo_fields_get', args),
|
|
115
|
+
latency_ms,
|
|
116
|
+
status: 'error',
|
|
117
|
+
error: e.message,
|
|
118
|
+
});
|
|
119
|
+
return {
|
|
120
|
+
isError: true,
|
|
121
|
+
content: [
|
|
122
|
+
{
|
|
123
|
+
type: 'text',
|
|
124
|
+
text: JSON.stringify(formatMcpError(e)),
|
|
125
|
+
},
|
|
126
|
+
],
|
|
127
|
+
};
|
|
128
|
+
}
|
|
129
|
+
// Step 7: Non-OdooError — unexpected exception. Log + return as InternalError-shaped.
|
|
130
|
+
const message = e instanceof Error ? e.message : String(e);
|
|
131
|
+
const latency_ms = Date.now() - start;
|
|
132
|
+
logger.toolCall({
|
|
133
|
+
tool: 'odoo_fields_get',
|
|
134
|
+
args_sanitized: sanitizeArgs('odoo_fields_get', args),
|
|
135
|
+
latency_ms,
|
|
136
|
+
status: 'error',
|
|
137
|
+
error: 'InternalError',
|
|
138
|
+
});
|
|
139
|
+
return {
|
|
140
|
+
isError: true,
|
|
141
|
+
content: [
|
|
142
|
+
{
|
|
143
|
+
type: 'text',
|
|
144
|
+
text: JSON.stringify({
|
|
145
|
+
error_type: 'InternalError',
|
|
146
|
+
message: 'unexpected error',
|
|
147
|
+
detail: message,
|
|
148
|
+
}),
|
|
149
|
+
},
|
|
150
|
+
],
|
|
151
|
+
};
|
|
152
|
+
}
|
|
153
|
+
});
|
|
154
|
+
}
|
|
155
|
+
//# sourceMappingURL=introspect.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"introspect.js","sourceRoot":"","sources":["../../src/tools/introspect.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
|
|
2
|
+
import { type OdooSession } from '@netlinksinc/odoo-client';
|
|
3
|
+
import type { OdooClient } from '@netlinksinc/odoo-client';
|
|
4
|
+
import type { Logger } from '../logger.js';
|
|
5
|
+
/**
|
|
6
|
+
* Register all 6 ORM tools on the MCP server.
|
|
7
|
+
*
|
|
8
|
+
* Tools registered: odoo_search_read, odoo_read, odoo_create, odoo_write,
|
|
9
|
+
* odoo_unlink, odoo_search_count.
|
|
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.
|
|
14
|
+
*/
|
|
15
|
+
export declare function registerOrmTools(server: McpServer, client: OdooClient, session: OdooSession, logger: Logger): void;
|
|
16
|
+
//# sourceMappingURL=orm.d.ts.map
|
|
@@ -0,0 +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;AAK3D,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,cAAc,CAAC;AAmH3C;;;;;;;;;GASG;AACH,wBAAgB,gBAAgB,CAC9B,MAAM,EAAE,SAAS,EACjB,MAAM,EAAE,UAAU,EAClB,OAAO,EAAE,WAAW,EACpB,MAAM,EAAE,MAAM,GACb,IAAI,CAiHN"}
|