@littlebearapps/outlook-assistant 3.9.0 → 3.9.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/package.json +2 -1
- package/request-handler.js +144 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@littlebearapps/outlook-assistant",
|
|
3
|
-
"version": "3.9.
|
|
3
|
+
"version": "3.9.1",
|
|
4
4
|
"mcpName": "io.github.littlebearapps/outlook-assistant",
|
|
5
5
|
"description": "Outlook Assistant — MCP server with 22 tools for email, calendar, contacts, and settings via Microsoft Graph API",
|
|
6
6
|
"main": "index.js",
|
|
@@ -58,6 +58,7 @@
|
|
|
58
58
|
"files": [
|
|
59
59
|
"index.js",
|
|
60
60
|
"config.js",
|
|
61
|
+
"request-handler.js",
|
|
61
62
|
"outlook-auth-server.js",
|
|
62
63
|
"auth/",
|
|
63
64
|
"calendar/",
|
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* MCP request dispatcher for the Outlook Assistant server.
|
|
3
|
+
*
|
|
4
|
+
* Extracted from index.js so the dispatch + error-shaping logic is
|
|
5
|
+
* unit-testable without starting the stdio transport.
|
|
6
|
+
*
|
|
7
|
+
* IMPORTANT (#213): a `tools/call` that fails MUST return a visible MCP
|
|
8
|
+
* tool-error result (`{ content: [...], isError: true }`). Returning a
|
|
9
|
+
* content-less `{ error: {...} }` object gets coerced by the SDK into
|
|
10
|
+
* `{ content: [] }`, which the client renders as EMPTY OUTPUT — the exact
|
|
11
|
+
* symptom reported for device-code auth in a remote connector session.
|
|
12
|
+
*/
|
|
13
|
+
const config = require('./config');
|
|
14
|
+
const { coerceArgsAgainstSchema } = require('./utils/schema-coerce');
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Build the MCP fallbackRequestHandler for a given tool set.
|
|
18
|
+
* @param {Array<{name: string, description?: string, inputSchema?: object, annotations?: object, handler?: Function}>} TOOLS
|
|
19
|
+
* @returns {(request: object) => Promise<object>}
|
|
20
|
+
*/
|
|
21
|
+
function createRequestHandler(TOOLS) {
|
|
22
|
+
return async (request) => {
|
|
23
|
+
try {
|
|
24
|
+
const { method, params, id } = request;
|
|
25
|
+
console.error(`REQUEST: ${method} [${id}]`);
|
|
26
|
+
|
|
27
|
+
// Initialize handler
|
|
28
|
+
if (method === 'initialize') {
|
|
29
|
+
console.error(`INITIALIZE REQUEST: ID [${id}]`);
|
|
30
|
+
return {
|
|
31
|
+
protocolVersion: '2024-11-05',
|
|
32
|
+
capabilities: {
|
|
33
|
+
tools: TOOLS.reduce((acc, tool) => {
|
|
34
|
+
acc[tool.name] = {};
|
|
35
|
+
return acc;
|
|
36
|
+
}, {}),
|
|
37
|
+
},
|
|
38
|
+
serverInfo: {
|
|
39
|
+
name: config.SERVER_NAME,
|
|
40
|
+
version: config.SERVER_VERSION,
|
|
41
|
+
},
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
// Tools list handler
|
|
46
|
+
if (method === 'tools/list') {
|
|
47
|
+
console.error(`TOOLS LIST REQUEST: ID [${id}]`);
|
|
48
|
+
console.error(`TOOLS COUNT: ${TOOLS.length}`);
|
|
49
|
+
console.error(`TOOLS NAMES: ${TOOLS.map((t) => t.name).join(', ')}`);
|
|
50
|
+
|
|
51
|
+
return {
|
|
52
|
+
tools: TOOLS.map((tool) => ({
|
|
53
|
+
name: tool.name,
|
|
54
|
+
description: tool.description,
|
|
55
|
+
inputSchema: tool.inputSchema,
|
|
56
|
+
...(tool.annotations && { annotations: tool.annotations }),
|
|
57
|
+
})),
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
// Required empty responses for other capabilities
|
|
62
|
+
if (method === 'resources/list') return { resources: [] };
|
|
63
|
+
if (method === 'prompts/list') return { prompts: [] };
|
|
64
|
+
|
|
65
|
+
// Tool call handler
|
|
66
|
+
if (method === 'tools/call') {
|
|
67
|
+
try {
|
|
68
|
+
const { name, arguments: args = {} } = params || {};
|
|
69
|
+
|
|
70
|
+
console.error(`TOOL CALL: ${name}`);
|
|
71
|
+
|
|
72
|
+
// Find the tool handler
|
|
73
|
+
const tool = TOOLS.find((t) => t.name === name);
|
|
74
|
+
|
|
75
|
+
if (tool && tool.handler) {
|
|
76
|
+
// Coerce + validate args against the tool's inputSchema before
|
|
77
|
+
// dispatching. Catches array-as-string, boolean-as-string, unknown
|
|
78
|
+
// params, and out-of-enum action values at the MCP boundary so
|
|
79
|
+
// handlers receive properly-typed JS values. (#160, #162)
|
|
80
|
+
if (tool.inputSchema) {
|
|
81
|
+
const coerced = coerceArgsAgainstSchema(args, tool.inputSchema);
|
|
82
|
+
if (coerced.error) {
|
|
83
|
+
return {
|
|
84
|
+
content: [
|
|
85
|
+
{
|
|
86
|
+
type: 'text',
|
|
87
|
+
text: `Invalid arguments for tool '${name}':\n${coerced.error}`,
|
|
88
|
+
},
|
|
89
|
+
],
|
|
90
|
+
isError: true,
|
|
91
|
+
};
|
|
92
|
+
}
|
|
93
|
+
return await tool.handler(coerced.args);
|
|
94
|
+
}
|
|
95
|
+
return await tool.handler(args);
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
// Tool not found — return visible isError content, not a
|
|
99
|
+
// content-less { error } (which renders as empty output). (#213)
|
|
100
|
+
return {
|
|
101
|
+
content: [
|
|
102
|
+
{
|
|
103
|
+
type: 'text',
|
|
104
|
+
text: `Tool not found: ${name}`,
|
|
105
|
+
},
|
|
106
|
+
],
|
|
107
|
+
isError: true,
|
|
108
|
+
};
|
|
109
|
+
} catch (error) {
|
|
110
|
+
console.error(`Error in tools/call:`, error);
|
|
111
|
+
// Surface the failure as visible tool-error content so it is not
|
|
112
|
+
// silently rendered as empty output by the client. (#213)
|
|
113
|
+
return {
|
|
114
|
+
content: [
|
|
115
|
+
{
|
|
116
|
+
type: 'text',
|
|
117
|
+
text: `Error processing tool call: ${error.message}`,
|
|
118
|
+
},
|
|
119
|
+
],
|
|
120
|
+
isError: true,
|
|
121
|
+
};
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
// For any other method, return method not found
|
|
126
|
+
return {
|
|
127
|
+
error: {
|
|
128
|
+
code: -32601,
|
|
129
|
+
message: `Method not found: ${method}`,
|
|
130
|
+
},
|
|
131
|
+
};
|
|
132
|
+
} catch (error) {
|
|
133
|
+
console.error(`Error in fallbackRequestHandler:`, error);
|
|
134
|
+
return {
|
|
135
|
+
error: {
|
|
136
|
+
code: -32603,
|
|
137
|
+
message: `Error processing request: ${error.message}`,
|
|
138
|
+
},
|
|
139
|
+
};
|
|
140
|
+
}
|
|
141
|
+
};
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
module.exports = { createRequestHandler };
|