ioffice-mcp 1.0.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/dist/client.js +65 -0
- package/dist/index.js +70 -0
- package/dist/tools/buildings.js +118 -0
- package/dist/tools/floors.js +108 -0
- package/dist/tools/mail.js +112 -0
- package/dist/tools/maintenance.js +165 -0
- package/dist/tools/moves.js +135 -0
- package/dist/tools/reservations.js +147 -0
- package/dist/tools/spaces.js +110 -0
- package/dist/tools/users.js +112 -0
- package/dist/tools/visitors.js +135 -0
- package/package.json +34 -0
package/dist/client.js
ADDED
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import { config as loadDotenv } from 'dotenv';
|
|
2
|
+
import { dirname, join } from 'path';
|
|
3
|
+
import { fileURLToPath } from 'url';
|
|
4
|
+
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
5
|
+
loadDotenv({ path: join(__dirname, '..', '.env'), override: false, quiet: true });
|
|
6
|
+
export class IOfficeClient {
|
|
7
|
+
baseUrl;
|
|
8
|
+
authHeaders;
|
|
9
|
+
constructor() {
|
|
10
|
+
const host = process.env.IOFFICE_HOST;
|
|
11
|
+
if (!host)
|
|
12
|
+
throw new Error('IOFFICE_HOST environment variable is required');
|
|
13
|
+
this.baseUrl = `https://${host}/external/api/rest/v2`;
|
|
14
|
+
const token = process.env.IOFFICE_TOKEN;
|
|
15
|
+
const username = process.env.IOFFICE_USERNAME;
|
|
16
|
+
const password = process.env.IOFFICE_PASSWORD;
|
|
17
|
+
if (token) {
|
|
18
|
+
this.authHeaders = { 'x-auth-token': token };
|
|
19
|
+
}
|
|
20
|
+
else if (username && password) {
|
|
21
|
+
this.authHeaders = { 'x-auth-username': username, 'x-auth-password': password };
|
|
22
|
+
}
|
|
23
|
+
else {
|
|
24
|
+
throw new Error('Authentication required: set IOFFICE_TOKEN, or both IOFFICE_USERNAME and IOFFICE_PASSWORD');
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
async request(method, path, body) {
|
|
28
|
+
return this.doRequest(method, path, body, false);
|
|
29
|
+
}
|
|
30
|
+
async doRequest(method, path, body, isRetry) {
|
|
31
|
+
const headers = {
|
|
32
|
+
...this.authHeaders,
|
|
33
|
+
'Content-Type': 'application/json',
|
|
34
|
+
Accept: 'application/json',
|
|
35
|
+
};
|
|
36
|
+
const response = await fetch(`${this.baseUrl}${path}`, {
|
|
37
|
+
method,
|
|
38
|
+
headers,
|
|
39
|
+
...(body !== undefined ? { body: JSON.stringify(body) } : {}),
|
|
40
|
+
});
|
|
41
|
+
if (response.status === 401) {
|
|
42
|
+
throw new Error('iOffice credentials are invalid (check IOFFICE_TOKEN or IOFFICE_USERNAME/IOFFICE_PASSWORD)');
|
|
43
|
+
}
|
|
44
|
+
if (response.status === 429) {
|
|
45
|
+
if (!isRetry) {
|
|
46
|
+
await new Promise((r) => setTimeout(r, 2000));
|
|
47
|
+
return this.doRequest(method, path, body, true);
|
|
48
|
+
}
|
|
49
|
+
throw new Error('Rate limited by iOffice API');
|
|
50
|
+
}
|
|
51
|
+
if (!response.ok) {
|
|
52
|
+
throw new Error(`iOffice API error: ${response.status} ${response.statusText} for ${method} ${path}`);
|
|
53
|
+
}
|
|
54
|
+
return response.json();
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
export function buildQueryString(params) {
|
|
58
|
+
const parts = [];
|
|
59
|
+
for (const [key, value] of Object.entries(params)) {
|
|
60
|
+
if (value !== undefined && value !== null && value !== '') {
|
|
61
|
+
parts.push(`${encodeURIComponent(key)}=${encodeURIComponent(String(value))}`);
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
return parts.length > 0 ? `?${parts.join('&')}` : '';
|
|
65
|
+
}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { Server } from '@modelcontextprotocol/sdk/server/index.js';
|
|
3
|
+
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
|
|
4
|
+
import { ListToolsRequestSchema, CallToolRequestSchema, } from '@modelcontextprotocol/sdk/types.js';
|
|
5
|
+
import { IOfficeClient } from './client.js';
|
|
6
|
+
import { toolDefinitions as buildingTools, handleTool as handleBuildings } from './tools/buildings.js';
|
|
7
|
+
import { toolDefinitions as floorTools, handleTool as handleFloors } from './tools/floors.js';
|
|
8
|
+
import { toolDefinitions as spaceTools, handleTool as handleSpaces } from './tools/spaces.js';
|
|
9
|
+
import { toolDefinitions as userTools, handleTool as handleUsers } from './tools/users.js';
|
|
10
|
+
import { toolDefinitions as reservationTools, handleTool as handleReservations } from './tools/reservations.js';
|
|
11
|
+
import { toolDefinitions as visitorTools, handleTool as handleVisitors } from './tools/visitors.js';
|
|
12
|
+
import { toolDefinitions as maintenanceTools, handleTool as handleMaintenance } from './tools/maintenance.js';
|
|
13
|
+
import { toolDefinitions as mailTools, handleTool as handleMail } from './tools/mail.js';
|
|
14
|
+
import { toolDefinitions as moveTools, handleTool as handleMoves } from './tools/moves.js';
|
|
15
|
+
const client = new IOfficeClient();
|
|
16
|
+
const allTools = [
|
|
17
|
+
...buildingTools,
|
|
18
|
+
...floorTools,
|
|
19
|
+
...spaceTools,
|
|
20
|
+
...userTools,
|
|
21
|
+
...reservationTools,
|
|
22
|
+
...visitorTools,
|
|
23
|
+
...maintenanceTools,
|
|
24
|
+
...mailTools,
|
|
25
|
+
...moveTools,
|
|
26
|
+
];
|
|
27
|
+
const handlers = {};
|
|
28
|
+
for (const tool of buildingTools)
|
|
29
|
+
handlers[tool.name] = (n, a) => handleBuildings(n, a, client);
|
|
30
|
+
for (const tool of floorTools)
|
|
31
|
+
handlers[tool.name] = (n, a) => handleFloors(n, a, client);
|
|
32
|
+
for (const tool of spaceTools)
|
|
33
|
+
handlers[tool.name] = (n, a) => handleSpaces(n, a, client);
|
|
34
|
+
for (const tool of userTools)
|
|
35
|
+
handlers[tool.name] = (n, a) => handleUsers(n, a, client);
|
|
36
|
+
for (const tool of reservationTools)
|
|
37
|
+
handlers[tool.name] = (n, a) => handleReservations(n, a, client);
|
|
38
|
+
for (const tool of visitorTools)
|
|
39
|
+
handlers[tool.name] = (n, a) => handleVisitors(n, a, client);
|
|
40
|
+
for (const tool of maintenanceTools)
|
|
41
|
+
handlers[tool.name] = (n, a) => handleMaintenance(n, a, client);
|
|
42
|
+
for (const tool of mailTools)
|
|
43
|
+
handlers[tool.name] = (n, a) => handleMail(n, a, client);
|
|
44
|
+
for (const tool of moveTools)
|
|
45
|
+
handlers[tool.name] = (n, a) => handleMoves(n, a, client);
|
|
46
|
+
const server = new Server({ name: 'ioffice', version: '1.0.0' }, { capabilities: { tools: {} } });
|
|
47
|
+
server.setRequestHandler(ListToolsRequestSchema, async () => ({ tools: allTools }));
|
|
48
|
+
server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
49
|
+
const { name, arguments: args = {} } = request.params;
|
|
50
|
+
const handler = handlers[name];
|
|
51
|
+
if (!handler) {
|
|
52
|
+
return {
|
|
53
|
+
content: [{ type: 'text', text: `Unknown tool: ${name}` }],
|
|
54
|
+
isError: true,
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
try {
|
|
58
|
+
return await handler(name, args);
|
|
59
|
+
}
|
|
60
|
+
catch (err) {
|
|
61
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
62
|
+
return {
|
|
63
|
+
content: [{ type: 'text', text: `Error: ${message}` }],
|
|
64
|
+
isError: true,
|
|
65
|
+
};
|
|
66
|
+
}
|
|
67
|
+
});
|
|
68
|
+
console.error('[ioffice-mcp] This project was developed and is maintained by AI (Claude Sonnet 4.6). Use at your own discretion.');
|
|
69
|
+
const transport = new StdioServerTransport();
|
|
70
|
+
await server.connect(transport);
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
import { buildQueryString } from '../client.js';
|
|
2
|
+
export const toolDefinitions = [
|
|
3
|
+
{
|
|
4
|
+
name: 'io_list_buildings',
|
|
5
|
+
description: 'List iOffice buildings. Supports search, pagination, and sorting.',
|
|
6
|
+
annotations: { readOnlyHint: true },
|
|
7
|
+
inputSchema: {
|
|
8
|
+
type: 'object',
|
|
9
|
+
properties: {
|
|
10
|
+
search: { type: 'string', description: 'Filter by name or description' },
|
|
11
|
+
limit: { type: 'number', description: 'Max results (default 50, max 100)' },
|
|
12
|
+
startAt: { type: 'number', description: 'Pagination offset (default 0)' },
|
|
13
|
+
orderBy: { type: 'string', description: 'Property to sort by (default: id)' },
|
|
14
|
+
orderByType: { type: 'string', enum: ['asc', 'desc'], description: 'Sort direction (default: asc)' },
|
|
15
|
+
},
|
|
16
|
+
required: [],
|
|
17
|
+
},
|
|
18
|
+
},
|
|
19
|
+
{
|
|
20
|
+
name: 'io_get_building',
|
|
21
|
+
description: 'Get a single iOffice building by ID.',
|
|
22
|
+
annotations: { readOnlyHint: true },
|
|
23
|
+
inputSchema: {
|
|
24
|
+
type: 'object',
|
|
25
|
+
properties: {
|
|
26
|
+
id: { type: 'number', description: 'Building ID' },
|
|
27
|
+
},
|
|
28
|
+
required: ['id'],
|
|
29
|
+
},
|
|
30
|
+
},
|
|
31
|
+
{
|
|
32
|
+
name: 'io_create_building',
|
|
33
|
+
description: 'Create a new iOffice building.',
|
|
34
|
+
annotations: { readOnlyHint: false },
|
|
35
|
+
inputSchema: {
|
|
36
|
+
type: 'object',
|
|
37
|
+
properties: {
|
|
38
|
+
name: { type: 'string', description: 'Building name' },
|
|
39
|
+
description: { type: 'string', description: 'Building description' },
|
|
40
|
+
address1: { type: 'string', description: 'Street address line 1' },
|
|
41
|
+
address2: { type: 'string', description: 'Street address line 2' },
|
|
42
|
+
city: { type: 'string', description: 'City' },
|
|
43
|
+
state: { type: 'string', description: 'State or province' },
|
|
44
|
+
postalCode: { type: 'string', description: 'Postal/ZIP code' },
|
|
45
|
+
country: { type: 'string', description: 'Country code' },
|
|
46
|
+
phone: { type: 'string', description: 'Building phone number' },
|
|
47
|
+
totalSquareFootage: { type: 'number', description: 'Total square footage' },
|
|
48
|
+
},
|
|
49
|
+
required: ['name'],
|
|
50
|
+
},
|
|
51
|
+
},
|
|
52
|
+
{
|
|
53
|
+
name: 'io_update_building',
|
|
54
|
+
description: 'Update an existing iOffice building. Only provide fields to change.',
|
|
55
|
+
annotations: { readOnlyHint: false },
|
|
56
|
+
inputSchema: {
|
|
57
|
+
type: 'object',
|
|
58
|
+
properties: {
|
|
59
|
+
id: { type: 'number', description: 'Building ID' },
|
|
60
|
+
name: { type: 'string', description: 'Building name' },
|
|
61
|
+
description: { type: 'string', description: 'Building description' },
|
|
62
|
+
address1: { type: 'string', description: 'Street address line 1' },
|
|
63
|
+
address2: { type: 'string', description: 'Street address line 2' },
|
|
64
|
+
city: { type: 'string', description: 'City' },
|
|
65
|
+
state: { type: 'string', description: 'State or province' },
|
|
66
|
+
postalCode: { type: 'string', description: 'Postal/ZIP code' },
|
|
67
|
+
country: { type: 'string', description: 'Country code' },
|
|
68
|
+
phone: { type: 'string', description: 'Building phone number' },
|
|
69
|
+
totalSquareFootage: { type: 'number', description: 'Total square footage' },
|
|
70
|
+
},
|
|
71
|
+
required: ['id'],
|
|
72
|
+
},
|
|
73
|
+
},
|
|
74
|
+
{
|
|
75
|
+
name: 'io_delete_building',
|
|
76
|
+
description: 'Delete an iOffice building by ID.',
|
|
77
|
+
annotations: { readOnlyHint: false, destructiveHint: true },
|
|
78
|
+
inputSchema: {
|
|
79
|
+
type: 'object',
|
|
80
|
+
properties: {
|
|
81
|
+
id: { type: 'number', description: 'Building ID' },
|
|
82
|
+
},
|
|
83
|
+
required: ['id'],
|
|
84
|
+
},
|
|
85
|
+
},
|
|
86
|
+
];
|
|
87
|
+
export async function handleTool(name, args, client) {
|
|
88
|
+
switch (name) {
|
|
89
|
+
case 'io_list_buildings': {
|
|
90
|
+
const { search, limit, startAt, orderBy, orderByType } = args;
|
|
91
|
+
const qs = buildQueryString({ search, limit, startAt, orderBy, orderByType });
|
|
92
|
+
const data = await client.request('GET', `/buildings${qs}`);
|
|
93
|
+
return { content: [{ type: 'text', text: JSON.stringify(data, null, 2) }] };
|
|
94
|
+
}
|
|
95
|
+
case 'io_get_building': {
|
|
96
|
+
const { id } = args;
|
|
97
|
+
const data = await client.request('GET', `/buildings/${id}`);
|
|
98
|
+
return { content: [{ type: 'text', text: JSON.stringify(data, null, 2) }] };
|
|
99
|
+
}
|
|
100
|
+
case 'io_create_building': {
|
|
101
|
+
const { id: _id, ...body } = args;
|
|
102
|
+
const data = await client.request('POST', '/buildings', body);
|
|
103
|
+
return { content: [{ type: 'text', text: JSON.stringify(data, null, 2) }] };
|
|
104
|
+
}
|
|
105
|
+
case 'io_update_building': {
|
|
106
|
+
const { id, ...body } = args;
|
|
107
|
+
const data = await client.request('PUT', `/buildings/${id}`, body);
|
|
108
|
+
return { content: [{ type: 'text', text: JSON.stringify(data, null, 2) }] };
|
|
109
|
+
}
|
|
110
|
+
case 'io_delete_building': {
|
|
111
|
+
const { id } = args;
|
|
112
|
+
const data = await client.request('DELETE', `/buildings/${id}`);
|
|
113
|
+
return { content: [{ type: 'text', text: JSON.stringify(data, null, 2) }] };
|
|
114
|
+
}
|
|
115
|
+
default:
|
|
116
|
+
throw new Error(`Unknown tool: ${name}`);
|
|
117
|
+
}
|
|
118
|
+
}
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
import { buildQueryString } from '../client.js';
|
|
2
|
+
export const toolDefinitions = [
|
|
3
|
+
{
|
|
4
|
+
name: 'io_list_floors',
|
|
5
|
+
description: 'List iOffice floors. Optionally filter by building ID.',
|
|
6
|
+
annotations: { readOnlyHint: true },
|
|
7
|
+
inputSchema: {
|
|
8
|
+
type: 'object',
|
|
9
|
+
properties: {
|
|
10
|
+
buildingId: { type: 'number', description: 'Filter floors by building ID' },
|
|
11
|
+
search: { type: 'string', description: 'Filter by name' },
|
|
12
|
+
limit: { type: 'number', description: 'Max results (default 50, max 100)' },
|
|
13
|
+
startAt: { type: 'number', description: 'Pagination offset (default 0)' },
|
|
14
|
+
orderBy: { type: 'string', description: 'Property to sort by (default: id)' },
|
|
15
|
+
orderByType: { type: 'string', enum: ['asc', 'desc'], description: 'Sort direction (default: asc)' },
|
|
16
|
+
},
|
|
17
|
+
required: [],
|
|
18
|
+
},
|
|
19
|
+
},
|
|
20
|
+
{
|
|
21
|
+
name: 'io_get_floor',
|
|
22
|
+
description: 'Get a single iOffice floor by ID.',
|
|
23
|
+
annotations: { readOnlyHint: true },
|
|
24
|
+
inputSchema: {
|
|
25
|
+
type: 'object',
|
|
26
|
+
properties: {
|
|
27
|
+
id: { type: 'number', description: 'Floor ID' },
|
|
28
|
+
},
|
|
29
|
+
required: ['id'],
|
|
30
|
+
},
|
|
31
|
+
},
|
|
32
|
+
{
|
|
33
|
+
name: 'io_create_floor',
|
|
34
|
+
description: 'Create a new iOffice floor within a building.',
|
|
35
|
+
annotations: { readOnlyHint: false },
|
|
36
|
+
inputSchema: {
|
|
37
|
+
type: 'object',
|
|
38
|
+
properties: {
|
|
39
|
+
name: { type: 'string', description: 'Floor name' },
|
|
40
|
+
buildingId: { type: 'number', description: 'ID of the building this floor belongs to' },
|
|
41
|
+
description: { type: 'string', description: 'Floor description' },
|
|
42
|
+
totalSquareFootage: { type: 'number', description: 'Total square footage of the floor' },
|
|
43
|
+
floorNumber: { type: 'number', description: 'Physical floor number' },
|
|
44
|
+
},
|
|
45
|
+
required: ['name', 'buildingId'],
|
|
46
|
+
},
|
|
47
|
+
},
|
|
48
|
+
{
|
|
49
|
+
name: 'io_update_floor',
|
|
50
|
+
description: 'Update an existing iOffice floor. Only provide fields to change.',
|
|
51
|
+
annotations: { readOnlyHint: false },
|
|
52
|
+
inputSchema: {
|
|
53
|
+
type: 'object',
|
|
54
|
+
properties: {
|
|
55
|
+
id: { type: 'number', description: 'Floor ID' },
|
|
56
|
+
name: { type: 'string', description: 'Floor name' },
|
|
57
|
+
description: { type: 'string', description: 'Floor description' },
|
|
58
|
+
totalSquareFootage: { type: 'number', description: 'Total square footage' },
|
|
59
|
+
floorNumber: { type: 'number', description: 'Physical floor number' },
|
|
60
|
+
},
|
|
61
|
+
required: ['id'],
|
|
62
|
+
},
|
|
63
|
+
},
|
|
64
|
+
{
|
|
65
|
+
name: 'io_delete_floor',
|
|
66
|
+
description: 'Delete an iOffice floor by ID.',
|
|
67
|
+
annotations: { readOnlyHint: false, destructiveHint: true },
|
|
68
|
+
inputSchema: {
|
|
69
|
+
type: 'object',
|
|
70
|
+
properties: {
|
|
71
|
+
id: { type: 'number', description: 'Floor ID' },
|
|
72
|
+
},
|
|
73
|
+
required: ['id'],
|
|
74
|
+
},
|
|
75
|
+
},
|
|
76
|
+
];
|
|
77
|
+
export async function handleTool(name, args, client) {
|
|
78
|
+
switch (name) {
|
|
79
|
+
case 'io_list_floors': {
|
|
80
|
+
const { buildingId, search, limit, startAt, orderBy, orderByType } = args;
|
|
81
|
+
const qs = buildQueryString({ search, limit, startAt, orderBy, orderByType });
|
|
82
|
+
const path = buildingId ? `/buildings/${buildingId}/floors${qs}` : `/floors${qs}`;
|
|
83
|
+
const data = await client.request('GET', path);
|
|
84
|
+
return { content: [{ type: 'text', text: JSON.stringify(data, null, 2) }] };
|
|
85
|
+
}
|
|
86
|
+
case 'io_get_floor': {
|
|
87
|
+
const { id } = args;
|
|
88
|
+
const data = await client.request('GET', `/floors/${id}`);
|
|
89
|
+
return { content: [{ type: 'text', text: JSON.stringify(data, null, 2) }] };
|
|
90
|
+
}
|
|
91
|
+
case 'io_create_floor': {
|
|
92
|
+
const data = await client.request('POST', '/floors', args);
|
|
93
|
+
return { content: [{ type: 'text', text: JSON.stringify(data, null, 2) }] };
|
|
94
|
+
}
|
|
95
|
+
case 'io_update_floor': {
|
|
96
|
+
const { id, ...body } = args;
|
|
97
|
+
const data = await client.request('PUT', `/floors/${id}`, body);
|
|
98
|
+
return { content: [{ type: 'text', text: JSON.stringify(data, null, 2) }] };
|
|
99
|
+
}
|
|
100
|
+
case 'io_delete_floor': {
|
|
101
|
+
const { id } = args;
|
|
102
|
+
const data = await client.request('DELETE', `/floors/${id}`);
|
|
103
|
+
return { content: [{ type: 'text', text: JSON.stringify(data, null, 2) }] };
|
|
104
|
+
}
|
|
105
|
+
default:
|
|
106
|
+
throw new Error(`Unknown tool: ${name}`);
|
|
107
|
+
}
|
|
108
|
+
}
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
import { buildQueryString } from '../client.js';
|
|
2
|
+
export const toolDefinitions = [
|
|
3
|
+
{
|
|
4
|
+
name: 'io_list_mail',
|
|
5
|
+
description: 'List iOffice mail items (packages and letters). Supports filtering and pagination.',
|
|
6
|
+
annotations: { readOnlyHint: true },
|
|
7
|
+
inputSchema: {
|
|
8
|
+
type: 'object',
|
|
9
|
+
properties: {
|
|
10
|
+
search: { type: 'string', description: 'Filter by recipient name, tracking number, or sender' },
|
|
11
|
+
status: { type: 'string', description: 'Filter by status (e.g. received, delivered, returned)' },
|
|
12
|
+
buildingId: { type: 'number', description: 'Filter by building ID' },
|
|
13
|
+
recipientId: { type: 'number', description: 'Filter by recipient user ID' },
|
|
14
|
+
startDate: { type: 'string', description: 'Filter mail received on or after this date (ISO 8601)' },
|
|
15
|
+
endDate: { type: 'string', description: 'Filter mail received on or before this date (ISO 8601)' },
|
|
16
|
+
limit: { type: 'number', description: 'Max results (default 50, max 100)' },
|
|
17
|
+
startAt: { type: 'number', description: 'Pagination offset (default 0)' },
|
|
18
|
+
orderBy: { type: 'string', description: 'Property to sort by (default: id)' },
|
|
19
|
+
orderByType: { type: 'string', enum: ['asc', 'desc'], description: 'Sort direction (default: asc)' },
|
|
20
|
+
},
|
|
21
|
+
required: [],
|
|
22
|
+
},
|
|
23
|
+
},
|
|
24
|
+
{
|
|
25
|
+
name: 'io_get_mail',
|
|
26
|
+
description: 'Get a single iOffice mail item by ID.',
|
|
27
|
+
annotations: { readOnlyHint: true },
|
|
28
|
+
inputSchema: {
|
|
29
|
+
type: 'object',
|
|
30
|
+
properties: {
|
|
31
|
+
id: { type: 'number', description: 'Mail item ID' },
|
|
32
|
+
},
|
|
33
|
+
required: ['id'],
|
|
34
|
+
},
|
|
35
|
+
},
|
|
36
|
+
{
|
|
37
|
+
name: 'io_create_mail',
|
|
38
|
+
description: 'Log a new mail item (package or letter) received in iOffice.',
|
|
39
|
+
annotations: { readOnlyHint: false },
|
|
40
|
+
inputSchema: {
|
|
41
|
+
type: 'object',
|
|
42
|
+
properties: {
|
|
43
|
+
recipientId: { type: 'number', description: 'Recipient user ID' },
|
|
44
|
+
buildingId: { type: 'number', description: 'Building where mail was received' },
|
|
45
|
+
trackingNumber: { type: 'string', description: 'Package tracking number' },
|
|
46
|
+
carrier: { type: 'string', description: 'Shipping carrier (e.g. UPS, FedEx, USPS)' },
|
|
47
|
+
description: { type: 'string', description: 'Description of the mail item' },
|
|
48
|
+
mailTypeId: { type: 'number', description: 'Mail type ID' },
|
|
49
|
+
receivedDate: { type: 'string', description: 'Date/time received (ISO 8601, defaults to now)' },
|
|
50
|
+
},
|
|
51
|
+
required: ['recipientId', 'buildingId'],
|
|
52
|
+
},
|
|
53
|
+
},
|
|
54
|
+
{
|
|
55
|
+
name: 'io_deliver_mail',
|
|
56
|
+
description: 'Mark an iOffice mail item as delivered to the recipient.',
|
|
57
|
+
annotations: { readOnlyHint: false },
|
|
58
|
+
inputSchema: {
|
|
59
|
+
type: 'object',
|
|
60
|
+
properties: {
|
|
61
|
+
id: { type: 'number', description: 'Mail item ID' },
|
|
62
|
+
deliveredDate: { type: 'string', description: 'Delivery date/time (ISO 8601, defaults to now)' },
|
|
63
|
+
signature: { type: 'string', description: 'Recipient signature or name confirmation' },
|
|
64
|
+
},
|
|
65
|
+
required: ['id'],
|
|
66
|
+
},
|
|
67
|
+
},
|
|
68
|
+
{
|
|
69
|
+
name: 'io_return_mail',
|
|
70
|
+
description: 'Mark an iOffice mail item as returned to sender.',
|
|
71
|
+
annotations: { readOnlyHint: false },
|
|
72
|
+
inputSchema: {
|
|
73
|
+
type: 'object',
|
|
74
|
+
properties: {
|
|
75
|
+
id: { type: 'number', description: 'Mail item ID' },
|
|
76
|
+
reason: { type: 'string', description: 'Reason for return' },
|
|
77
|
+
},
|
|
78
|
+
required: ['id'],
|
|
79
|
+
},
|
|
80
|
+
},
|
|
81
|
+
];
|
|
82
|
+
export async function handleTool(name, args, client) {
|
|
83
|
+
switch (name) {
|
|
84
|
+
case 'io_list_mail': {
|
|
85
|
+
const { search, status, buildingId, recipientId, startDate, endDate, limit, startAt, orderBy, orderByType } = args;
|
|
86
|
+
const qs = buildQueryString({ search, status, buildingId, recipientId, startDate, endDate, limit, startAt, orderBy, orderByType });
|
|
87
|
+
const data = await client.request('GET', `/mail${qs}`);
|
|
88
|
+
return { content: [{ type: 'text', text: JSON.stringify(data, null, 2) }] };
|
|
89
|
+
}
|
|
90
|
+
case 'io_get_mail': {
|
|
91
|
+
const { id } = args;
|
|
92
|
+
const data = await client.request('GET', `/mail/${id}`);
|
|
93
|
+
return { content: [{ type: 'text', text: JSON.stringify(data, null, 2) }] };
|
|
94
|
+
}
|
|
95
|
+
case 'io_create_mail': {
|
|
96
|
+
const data = await client.request('POST', '/mail', args);
|
|
97
|
+
return { content: [{ type: 'text', text: JSON.stringify(data, null, 2) }] };
|
|
98
|
+
}
|
|
99
|
+
case 'io_deliver_mail': {
|
|
100
|
+
const { id, ...body } = args;
|
|
101
|
+
const data = await client.request('POST', `/mail/${id}/deliver`, Object.keys(body).length > 0 ? body : undefined);
|
|
102
|
+
return { content: [{ type: 'text', text: JSON.stringify(data, null, 2) }] };
|
|
103
|
+
}
|
|
104
|
+
case 'io_return_mail': {
|
|
105
|
+
const { id, ...body } = args;
|
|
106
|
+
const data = await client.request('POST', `/mail/${id}/return`, Object.keys(body).length > 0 ? body : undefined);
|
|
107
|
+
return { content: [{ type: 'text', text: JSON.stringify(data, null, 2) }] };
|
|
108
|
+
}
|
|
109
|
+
default:
|
|
110
|
+
throw new Error(`Unknown tool: ${name}`);
|
|
111
|
+
}
|
|
112
|
+
}
|
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
import { buildQueryString } from '../client.js';
|
|
2
|
+
export const toolDefinitions = [
|
|
3
|
+
{
|
|
4
|
+
name: 'io_list_maintenance_requests',
|
|
5
|
+
description: 'List iOffice maintenance requests. Supports filtering by status, space, or building.',
|
|
6
|
+
annotations: { readOnlyHint: true },
|
|
7
|
+
inputSchema: {
|
|
8
|
+
type: 'object',
|
|
9
|
+
properties: {
|
|
10
|
+
search: { type: 'string', description: 'Filter by title or description' },
|
|
11
|
+
status: { type: 'string', description: 'Filter by status (e.g. pending, accepted, started, completed, archived)' },
|
|
12
|
+
spaceId: { type: 'number', description: 'Filter by space/room ID' },
|
|
13
|
+
buildingId: { type: 'number', description: 'Filter by building ID' },
|
|
14
|
+
assignedUserId: { type: 'number', description: 'Filter by assigned technician user ID' },
|
|
15
|
+
limit: { type: 'number', description: 'Max results (default 50, max 100)' },
|
|
16
|
+
startAt: { type: 'number', description: 'Pagination offset (default 0)' },
|
|
17
|
+
orderBy: { type: 'string', description: 'Property to sort by (default: id)' },
|
|
18
|
+
orderByType: { type: 'string', enum: ['asc', 'desc'], description: 'Sort direction (default: asc)' },
|
|
19
|
+
},
|
|
20
|
+
required: [],
|
|
21
|
+
},
|
|
22
|
+
},
|
|
23
|
+
{
|
|
24
|
+
name: 'io_get_maintenance_request',
|
|
25
|
+
description: 'Get a single iOffice maintenance request by ID.',
|
|
26
|
+
annotations: { readOnlyHint: true },
|
|
27
|
+
inputSchema: {
|
|
28
|
+
type: 'object',
|
|
29
|
+
properties: {
|
|
30
|
+
id: { type: 'number', description: 'Maintenance request ID' },
|
|
31
|
+
},
|
|
32
|
+
required: ['id'],
|
|
33
|
+
},
|
|
34
|
+
},
|
|
35
|
+
{
|
|
36
|
+
name: 'io_create_maintenance_request',
|
|
37
|
+
description: 'Create a new iOffice maintenance request.',
|
|
38
|
+
annotations: { readOnlyHint: false },
|
|
39
|
+
inputSchema: {
|
|
40
|
+
type: 'object',
|
|
41
|
+
properties: {
|
|
42
|
+
title: { type: 'string', description: 'Request title/summary' },
|
|
43
|
+
description: { type: 'string', description: 'Detailed description of the issue' },
|
|
44
|
+
spaceId: { type: 'number', description: 'Space/room ID where the issue is located' },
|
|
45
|
+
buildingId: { type: 'number', description: 'Building ID where the issue is located' },
|
|
46
|
+
priorityId: { type: 'number', description: 'Priority level ID' },
|
|
47
|
+
typeId: { type: 'number', description: 'Maintenance type/category ID' },
|
|
48
|
+
assignedUserId: { type: 'number', description: 'Technician user ID to assign' },
|
|
49
|
+
},
|
|
50
|
+
required: ['title'],
|
|
51
|
+
},
|
|
52
|
+
},
|
|
53
|
+
{
|
|
54
|
+
name: 'io_update_maintenance_request',
|
|
55
|
+
description: 'Update an existing iOffice maintenance request. Only provide fields to change.',
|
|
56
|
+
annotations: { readOnlyHint: false },
|
|
57
|
+
inputSchema: {
|
|
58
|
+
type: 'object',
|
|
59
|
+
properties: {
|
|
60
|
+
id: { type: 'number', description: 'Maintenance request ID' },
|
|
61
|
+
title: { type: 'string', description: 'Request title/summary' },
|
|
62
|
+
description: { type: 'string', description: 'Detailed description' },
|
|
63
|
+
priorityId: { type: 'number', description: 'Priority level ID' },
|
|
64
|
+
assignedUserId: { type: 'number', description: 'Assigned technician user ID' },
|
|
65
|
+
},
|
|
66
|
+
required: ['id'],
|
|
67
|
+
},
|
|
68
|
+
},
|
|
69
|
+
{
|
|
70
|
+
name: 'io_accept_maintenance_request',
|
|
71
|
+
description: 'Accept an iOffice maintenance request (transition from pending to accepted).',
|
|
72
|
+
annotations: { readOnlyHint: false },
|
|
73
|
+
inputSchema: {
|
|
74
|
+
type: 'object',
|
|
75
|
+
properties: {
|
|
76
|
+
id: { type: 'number', description: 'Maintenance request ID' },
|
|
77
|
+
},
|
|
78
|
+
required: ['id'],
|
|
79
|
+
},
|
|
80
|
+
},
|
|
81
|
+
{
|
|
82
|
+
name: 'io_start_maintenance_request',
|
|
83
|
+
description: 'Start work on an iOffice maintenance request (transition to started/in-progress).',
|
|
84
|
+
annotations: { readOnlyHint: false },
|
|
85
|
+
inputSchema: {
|
|
86
|
+
type: 'object',
|
|
87
|
+
properties: {
|
|
88
|
+
id: { type: 'number', description: 'Maintenance request ID' },
|
|
89
|
+
},
|
|
90
|
+
required: ['id'],
|
|
91
|
+
},
|
|
92
|
+
},
|
|
93
|
+
{
|
|
94
|
+
name: 'io_complete_maintenance_request',
|
|
95
|
+
description: 'Mark an iOffice maintenance request as complete.',
|
|
96
|
+
annotations: { readOnlyHint: false },
|
|
97
|
+
inputSchema: {
|
|
98
|
+
type: 'object',
|
|
99
|
+
properties: {
|
|
100
|
+
id: { type: 'number', description: 'Maintenance request ID' },
|
|
101
|
+
resolution: { type: 'string', description: 'Resolution notes describing what was done' },
|
|
102
|
+
},
|
|
103
|
+
required: ['id'],
|
|
104
|
+
},
|
|
105
|
+
},
|
|
106
|
+
{
|
|
107
|
+
name: 'io_archive_maintenance_request',
|
|
108
|
+
description: 'Archive a completed iOffice maintenance request.',
|
|
109
|
+
annotations: { readOnlyHint: false },
|
|
110
|
+
inputSchema: {
|
|
111
|
+
type: 'object',
|
|
112
|
+
properties: {
|
|
113
|
+
id: { type: 'number', description: 'Maintenance request ID' },
|
|
114
|
+
},
|
|
115
|
+
required: ['id'],
|
|
116
|
+
},
|
|
117
|
+
},
|
|
118
|
+
];
|
|
119
|
+
export async function handleTool(name, args, client) {
|
|
120
|
+
switch (name) {
|
|
121
|
+
case 'io_list_maintenance_requests': {
|
|
122
|
+
const { search, status, spaceId, buildingId, assignedUserId, limit, startAt, orderBy, orderByType } = args;
|
|
123
|
+
const qs = buildQueryString({ search, status, spaceId, buildingId, assignedUserId, limit, startAt, orderBy, orderByType });
|
|
124
|
+
const data = await client.request('GET', `/maintenanceRequests${qs}`);
|
|
125
|
+
return { content: [{ type: 'text', text: JSON.stringify(data, null, 2) }] };
|
|
126
|
+
}
|
|
127
|
+
case 'io_get_maintenance_request': {
|
|
128
|
+
const { id } = args;
|
|
129
|
+
const data = await client.request('GET', `/maintenanceRequests/${id}`);
|
|
130
|
+
return { content: [{ type: 'text', text: JSON.stringify(data, null, 2) }] };
|
|
131
|
+
}
|
|
132
|
+
case 'io_create_maintenance_request': {
|
|
133
|
+
const data = await client.request('POST', '/maintenanceRequests', args);
|
|
134
|
+
return { content: [{ type: 'text', text: JSON.stringify(data, null, 2) }] };
|
|
135
|
+
}
|
|
136
|
+
case 'io_update_maintenance_request': {
|
|
137
|
+
const { id, ...body } = args;
|
|
138
|
+
const data = await client.request('PUT', `/maintenanceRequests/${id}`, body);
|
|
139
|
+
return { content: [{ type: 'text', text: JSON.stringify(data, null, 2) }] };
|
|
140
|
+
}
|
|
141
|
+
case 'io_accept_maintenance_request': {
|
|
142
|
+
const { id } = args;
|
|
143
|
+
const data = await client.request('POST', `/maintenanceRequests/${id}/accept`);
|
|
144
|
+
return { content: [{ type: 'text', text: JSON.stringify(data, null, 2) }] };
|
|
145
|
+
}
|
|
146
|
+
case 'io_start_maintenance_request': {
|
|
147
|
+
const { id } = args;
|
|
148
|
+
const data = await client.request('POST', `/maintenanceRequests/${id}/start`);
|
|
149
|
+
return { content: [{ type: 'text', text: JSON.stringify(data, null, 2) }] };
|
|
150
|
+
}
|
|
151
|
+
case 'io_complete_maintenance_request': {
|
|
152
|
+
const { id, resolution } = args;
|
|
153
|
+
const body = resolution !== undefined ? { resolution } : undefined;
|
|
154
|
+
const data = await client.request('POST', `/maintenanceRequests/${id}/complete`, body);
|
|
155
|
+
return { content: [{ type: 'text', text: JSON.stringify(data, null, 2) }] };
|
|
156
|
+
}
|
|
157
|
+
case 'io_archive_maintenance_request': {
|
|
158
|
+
const { id } = args;
|
|
159
|
+
const data = await client.request('POST', `/maintenanceRequests/${id}/archive`);
|
|
160
|
+
return { content: [{ type: 'text', text: JSON.stringify(data, null, 2) }] };
|
|
161
|
+
}
|
|
162
|
+
default:
|
|
163
|
+
throw new Error(`Unknown tool: ${name}`);
|
|
164
|
+
}
|
|
165
|
+
}
|
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
import { buildQueryString } from '../client.js';
|
|
2
|
+
export const toolDefinitions = [
|
|
3
|
+
{
|
|
4
|
+
name: 'io_list_moves',
|
|
5
|
+
description: 'List iOffice move requests. Supports filtering by status, building, or assignee.',
|
|
6
|
+
annotations: { readOnlyHint: true },
|
|
7
|
+
inputSchema: {
|
|
8
|
+
type: 'object',
|
|
9
|
+
properties: {
|
|
10
|
+
search: { type: 'string', description: 'Filter by name or description' },
|
|
11
|
+
status: { type: 'string', description: 'Filter by status (e.g. pending, approved, completed)' },
|
|
12
|
+
buildingId: { type: 'number', description: 'Filter by building ID' },
|
|
13
|
+
requesterId: { type: 'number', description: 'Filter by requester user ID' },
|
|
14
|
+
startDate: { type: 'string', description: 'Filter moves on or after this date (ISO 8601)' },
|
|
15
|
+
endDate: { type: 'string', description: 'Filter moves on or before this date (ISO 8601)' },
|
|
16
|
+
limit: { type: 'number', description: 'Max results (default 50, max 100)' },
|
|
17
|
+
startAt: { type: 'number', description: 'Pagination offset (default 0)' },
|
|
18
|
+
orderBy: { type: 'string', description: 'Property to sort by (default: id)' },
|
|
19
|
+
orderByType: { type: 'string', enum: ['asc', 'desc'], description: 'Sort direction (default: asc)' },
|
|
20
|
+
},
|
|
21
|
+
required: [],
|
|
22
|
+
},
|
|
23
|
+
},
|
|
24
|
+
{
|
|
25
|
+
name: 'io_get_move',
|
|
26
|
+
description: 'Get a single iOffice move request by ID.',
|
|
27
|
+
annotations: { readOnlyHint: true },
|
|
28
|
+
inputSchema: {
|
|
29
|
+
type: 'object',
|
|
30
|
+
properties: {
|
|
31
|
+
id: { type: 'number', description: 'Move request ID' },
|
|
32
|
+
},
|
|
33
|
+
required: ['id'],
|
|
34
|
+
},
|
|
35
|
+
},
|
|
36
|
+
{
|
|
37
|
+
name: 'io_create_move',
|
|
38
|
+
description: 'Create a new iOffice move request.',
|
|
39
|
+
annotations: { readOnlyHint: false },
|
|
40
|
+
inputSchema: {
|
|
41
|
+
type: 'object',
|
|
42
|
+
properties: {
|
|
43
|
+
name: { type: 'string', description: 'Move request name/title' },
|
|
44
|
+
description: { type: 'string', description: 'Description of the move' },
|
|
45
|
+
requesterId: { type: 'number', description: 'User ID of the person requesting the move' },
|
|
46
|
+
fromSpaceId: { type: 'number', description: 'Source space/room ID' },
|
|
47
|
+
toSpaceId: { type: 'number', description: 'Destination space/room ID' },
|
|
48
|
+
scheduledDate: { type: 'string', description: 'Scheduled move date (ISO 8601)' },
|
|
49
|
+
buildingId: { type: 'number', description: 'Building ID where the move takes place' },
|
|
50
|
+
},
|
|
51
|
+
required: ['name'],
|
|
52
|
+
},
|
|
53
|
+
},
|
|
54
|
+
{
|
|
55
|
+
name: 'io_update_move',
|
|
56
|
+
description: 'Update an existing iOffice move request. Only provide fields to change.',
|
|
57
|
+
annotations: { readOnlyHint: false },
|
|
58
|
+
inputSchema: {
|
|
59
|
+
type: 'object',
|
|
60
|
+
properties: {
|
|
61
|
+
id: { type: 'number', description: 'Move request ID' },
|
|
62
|
+
name: { type: 'string', description: 'Move request name/title' },
|
|
63
|
+
description: { type: 'string', description: 'Description of the move' },
|
|
64
|
+
scheduledDate: { type: 'string', description: 'Scheduled move date (ISO 8601)' },
|
|
65
|
+
fromSpaceId: { type: 'number', description: 'Source space/room ID' },
|
|
66
|
+
toSpaceId: { type: 'number', description: 'Destination space/room ID' },
|
|
67
|
+
},
|
|
68
|
+
required: ['id'],
|
|
69
|
+
},
|
|
70
|
+
},
|
|
71
|
+
{
|
|
72
|
+
name: 'io_approve_move',
|
|
73
|
+
description: 'Approve an iOffice move request.',
|
|
74
|
+
annotations: { readOnlyHint: false },
|
|
75
|
+
inputSchema: {
|
|
76
|
+
type: 'object',
|
|
77
|
+
properties: {
|
|
78
|
+
id: { type: 'number', description: 'Move request ID' },
|
|
79
|
+
notes: { type: 'string', description: 'Approval notes (optional)' },
|
|
80
|
+
},
|
|
81
|
+
required: ['id'],
|
|
82
|
+
},
|
|
83
|
+
},
|
|
84
|
+
{
|
|
85
|
+
name: 'io_cancel_move',
|
|
86
|
+
description: 'Cancel an iOffice move request.',
|
|
87
|
+
annotations: { readOnlyHint: false, destructiveHint: true },
|
|
88
|
+
inputSchema: {
|
|
89
|
+
type: 'object',
|
|
90
|
+
properties: {
|
|
91
|
+
id: { type: 'number', description: 'Move request ID' },
|
|
92
|
+
reason: { type: 'string', description: 'Cancellation reason' },
|
|
93
|
+
},
|
|
94
|
+
required: ['id'],
|
|
95
|
+
},
|
|
96
|
+
},
|
|
97
|
+
];
|
|
98
|
+
export async function handleTool(name, args, client) {
|
|
99
|
+
switch (name) {
|
|
100
|
+
case 'io_list_moves': {
|
|
101
|
+
const { search, status, buildingId, requesterId, startDate, endDate, limit, startAt, orderBy, orderByType } = args;
|
|
102
|
+
const qs = buildQueryString({ search, status, buildingId, requesterId, startDate, endDate, limit, startAt, orderBy, orderByType });
|
|
103
|
+
const data = await client.request('GET', `/moves${qs}`);
|
|
104
|
+
return { content: [{ type: 'text', text: JSON.stringify(data, null, 2) }] };
|
|
105
|
+
}
|
|
106
|
+
case 'io_get_move': {
|
|
107
|
+
const { id } = args;
|
|
108
|
+
const data = await client.request('GET', `/moves/${id}`);
|
|
109
|
+
return { content: [{ type: 'text', text: JSON.stringify(data, null, 2) }] };
|
|
110
|
+
}
|
|
111
|
+
case 'io_create_move': {
|
|
112
|
+
const data = await client.request('POST', '/moves', args);
|
|
113
|
+
return { content: [{ type: 'text', text: JSON.stringify(data, null, 2) }] };
|
|
114
|
+
}
|
|
115
|
+
case 'io_update_move': {
|
|
116
|
+
const { id, ...body } = args;
|
|
117
|
+
const data = await client.request('PUT', `/moves/${id}`, body);
|
|
118
|
+
return { content: [{ type: 'text', text: JSON.stringify(data, null, 2) }] };
|
|
119
|
+
}
|
|
120
|
+
case 'io_approve_move': {
|
|
121
|
+
const { id, notes } = args;
|
|
122
|
+
const body = notes !== undefined ? { notes } : undefined;
|
|
123
|
+
const data = await client.request('POST', `/moves/${id}/approve`, body);
|
|
124
|
+
return { content: [{ type: 'text', text: JSON.stringify(data, null, 2) }] };
|
|
125
|
+
}
|
|
126
|
+
case 'io_cancel_move': {
|
|
127
|
+
const { id, reason } = args;
|
|
128
|
+
const body = reason !== undefined ? { reason } : undefined;
|
|
129
|
+
const data = await client.request('POST', `/moves/${id}/cancel`, body);
|
|
130
|
+
return { content: [{ type: 'text', text: JSON.stringify(data, null, 2) }] };
|
|
131
|
+
}
|
|
132
|
+
default:
|
|
133
|
+
throw new Error(`Unknown tool: ${name}`);
|
|
134
|
+
}
|
|
135
|
+
}
|
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
import { buildQueryString } from '../client.js';
|
|
2
|
+
export const toolDefinitions = [
|
|
3
|
+
{
|
|
4
|
+
name: 'io_list_reservations',
|
|
5
|
+
description: 'List iOffice reservations. Supports filtering by date range, space, or user.',
|
|
6
|
+
annotations: { readOnlyHint: true },
|
|
7
|
+
inputSchema: {
|
|
8
|
+
type: 'object',
|
|
9
|
+
properties: {
|
|
10
|
+
search: { type: 'string', description: 'Filter by title or description' },
|
|
11
|
+
startDate: { type: 'string', description: 'Filter reservations starting on or after this date (ISO 8601)' },
|
|
12
|
+
endDate: { type: 'string', description: 'Filter reservations ending on or before this date (ISO 8601)' },
|
|
13
|
+
spaceId: { type: 'number', description: 'Filter by space/room ID' },
|
|
14
|
+
userId: { type: 'number', description: 'Filter by organizer user ID' },
|
|
15
|
+
limit: { type: 'number', description: 'Max results (default 50, max 100)' },
|
|
16
|
+
startAt: { type: 'number', description: 'Pagination offset (default 0)' },
|
|
17
|
+
orderBy: { type: 'string', description: 'Property to sort by (default: id)' },
|
|
18
|
+
orderByType: { type: 'string', enum: ['asc', 'desc'], description: 'Sort direction (default: asc)' },
|
|
19
|
+
},
|
|
20
|
+
required: [],
|
|
21
|
+
},
|
|
22
|
+
},
|
|
23
|
+
{
|
|
24
|
+
name: 'io_get_reservation',
|
|
25
|
+
description: 'Get a single iOffice reservation by ID.',
|
|
26
|
+
annotations: { readOnlyHint: true },
|
|
27
|
+
inputSchema: {
|
|
28
|
+
type: 'object',
|
|
29
|
+
properties: {
|
|
30
|
+
id: { type: 'number', description: 'Reservation ID' },
|
|
31
|
+
},
|
|
32
|
+
required: ['id'],
|
|
33
|
+
},
|
|
34
|
+
},
|
|
35
|
+
{
|
|
36
|
+
name: 'io_create_reservation',
|
|
37
|
+
description: 'Create a new iOffice room/space reservation.',
|
|
38
|
+
annotations: { readOnlyHint: false },
|
|
39
|
+
inputSchema: {
|
|
40
|
+
type: 'object',
|
|
41
|
+
properties: {
|
|
42
|
+
title: { type: 'string', description: 'Reservation title/name' },
|
|
43
|
+
spaceId: { type: 'number', description: 'Space/room ID to reserve' },
|
|
44
|
+
startDate: { type: 'string', description: 'Start date/time (ISO 8601, e.g. 2026-03-20T09:00:00)' },
|
|
45
|
+
endDate: { type: 'string', description: 'End date/time (ISO 8601, e.g. 2026-03-20T10:00:00)' },
|
|
46
|
+
description: { type: 'string', description: 'Reservation notes or description' },
|
|
47
|
+
attendeeCount: { type: 'number', description: 'Expected number of attendees' },
|
|
48
|
+
userId: { type: 'number', description: 'Organizer user ID (defaults to authenticated user)' },
|
|
49
|
+
},
|
|
50
|
+
required: ['title', 'spaceId', 'startDate', 'endDate'],
|
|
51
|
+
},
|
|
52
|
+
},
|
|
53
|
+
{
|
|
54
|
+
name: 'io_update_reservation',
|
|
55
|
+
description: 'Update an existing iOffice reservation. Only provide fields to change.',
|
|
56
|
+
annotations: { readOnlyHint: false },
|
|
57
|
+
inputSchema: {
|
|
58
|
+
type: 'object',
|
|
59
|
+
properties: {
|
|
60
|
+
id: { type: 'number', description: 'Reservation ID' },
|
|
61
|
+
title: { type: 'string', description: 'Reservation title' },
|
|
62
|
+
startDate: { type: 'string', description: 'New start date/time (ISO 8601)' },
|
|
63
|
+
endDate: { type: 'string', description: 'New end date/time (ISO 8601)' },
|
|
64
|
+
description: { type: 'string', description: 'Notes or description' },
|
|
65
|
+
attendeeCount: { type: 'number', description: 'Expected number of attendees' },
|
|
66
|
+
},
|
|
67
|
+
required: ['id'],
|
|
68
|
+
},
|
|
69
|
+
},
|
|
70
|
+
{
|
|
71
|
+
name: 'io_delete_reservation',
|
|
72
|
+
description: 'Delete/cancel an iOffice reservation by ID.',
|
|
73
|
+
annotations: { readOnlyHint: false, destructiveHint: true },
|
|
74
|
+
inputSchema: {
|
|
75
|
+
type: 'object',
|
|
76
|
+
properties: {
|
|
77
|
+
id: { type: 'number', description: 'Reservation ID' },
|
|
78
|
+
},
|
|
79
|
+
required: ['id'],
|
|
80
|
+
},
|
|
81
|
+
},
|
|
82
|
+
{
|
|
83
|
+
name: 'io_checkin_reservation',
|
|
84
|
+
description: 'Check in to an iOffice reservation, confirming room usage.',
|
|
85
|
+
annotations: { readOnlyHint: false },
|
|
86
|
+
inputSchema: {
|
|
87
|
+
type: 'object',
|
|
88
|
+
properties: {
|
|
89
|
+
id: { type: 'number', description: 'Reservation ID' },
|
|
90
|
+
},
|
|
91
|
+
required: ['id'],
|
|
92
|
+
},
|
|
93
|
+
},
|
|
94
|
+
{
|
|
95
|
+
name: 'io_checkout_reservation',
|
|
96
|
+
description: 'Check out of an iOffice reservation, releasing the room early if needed.',
|
|
97
|
+
annotations: { readOnlyHint: false },
|
|
98
|
+
inputSchema: {
|
|
99
|
+
type: 'object',
|
|
100
|
+
properties: {
|
|
101
|
+
id: { type: 'number', description: 'Reservation ID' },
|
|
102
|
+
},
|
|
103
|
+
required: ['id'],
|
|
104
|
+
},
|
|
105
|
+
},
|
|
106
|
+
];
|
|
107
|
+
export async function handleTool(name, args, client) {
|
|
108
|
+
switch (name) {
|
|
109
|
+
case 'io_list_reservations': {
|
|
110
|
+
const { search, startDate, endDate, spaceId, userId, limit, startAt, orderBy, orderByType } = args;
|
|
111
|
+
const qs = buildQueryString({ search, startDate, endDate, spaceId, userId, limit, startAt, orderBy, orderByType });
|
|
112
|
+
const data = await client.request('GET', `/reservations${qs}`);
|
|
113
|
+
return { content: [{ type: 'text', text: JSON.stringify(data, null, 2) }] };
|
|
114
|
+
}
|
|
115
|
+
case 'io_get_reservation': {
|
|
116
|
+
const { id } = args;
|
|
117
|
+
const data = await client.request('GET', `/reservations/${id}`);
|
|
118
|
+
return { content: [{ type: 'text', text: JSON.stringify(data, null, 2) }] };
|
|
119
|
+
}
|
|
120
|
+
case 'io_create_reservation': {
|
|
121
|
+
const data = await client.request('POST', '/reservations', args);
|
|
122
|
+
return { content: [{ type: 'text', text: JSON.stringify(data, null, 2) }] };
|
|
123
|
+
}
|
|
124
|
+
case 'io_update_reservation': {
|
|
125
|
+
const { id, ...body } = args;
|
|
126
|
+
const data = await client.request('PUT', `/reservations/${id}`, body);
|
|
127
|
+
return { content: [{ type: 'text', text: JSON.stringify(data, null, 2) }] };
|
|
128
|
+
}
|
|
129
|
+
case 'io_delete_reservation': {
|
|
130
|
+
const { id } = args;
|
|
131
|
+
const data = await client.request('DELETE', `/reservations/${id}`);
|
|
132
|
+
return { content: [{ type: 'text', text: JSON.stringify(data, null, 2) }] };
|
|
133
|
+
}
|
|
134
|
+
case 'io_checkin_reservation': {
|
|
135
|
+
const { id } = args;
|
|
136
|
+
const data = await client.request('POST', `/reservations/${id}/checkIn`);
|
|
137
|
+
return { content: [{ type: 'text', text: JSON.stringify(data, null, 2) }] };
|
|
138
|
+
}
|
|
139
|
+
case 'io_checkout_reservation': {
|
|
140
|
+
const { id } = args;
|
|
141
|
+
const data = await client.request('POST', `/reservations/${id}/checkOut`);
|
|
142
|
+
return { content: [{ type: 'text', text: JSON.stringify(data, null, 2) }] };
|
|
143
|
+
}
|
|
144
|
+
default:
|
|
145
|
+
throw new Error(`Unknown tool: ${name}`);
|
|
146
|
+
}
|
|
147
|
+
}
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
import { buildQueryString } from '../client.js';
|
|
2
|
+
export const toolDefinitions = [
|
|
3
|
+
{
|
|
4
|
+
name: 'io_list_spaces',
|
|
5
|
+
description: 'List iOffice spaces (rooms). Optionally filter by floor ID.',
|
|
6
|
+
annotations: { readOnlyHint: true },
|
|
7
|
+
inputSchema: {
|
|
8
|
+
type: 'object',
|
|
9
|
+
properties: {
|
|
10
|
+
floorId: { type: 'number', description: 'Filter spaces by floor ID' },
|
|
11
|
+
search: { type: 'string', description: 'Filter by name or description' },
|
|
12
|
+
limit: { type: 'number', description: 'Max results (default 50, max 100)' },
|
|
13
|
+
startAt: { type: 'number', description: 'Pagination offset (default 0)' },
|
|
14
|
+
orderBy: { type: 'string', description: 'Property to sort by (default: id)' },
|
|
15
|
+
orderByType: { type: 'string', enum: ['asc', 'desc'], description: 'Sort direction (default: asc)' },
|
|
16
|
+
},
|
|
17
|
+
required: [],
|
|
18
|
+
},
|
|
19
|
+
},
|
|
20
|
+
{
|
|
21
|
+
name: 'io_get_space',
|
|
22
|
+
description: 'Get a single iOffice space (room) by ID.',
|
|
23
|
+
annotations: { readOnlyHint: true },
|
|
24
|
+
inputSchema: {
|
|
25
|
+
type: 'object',
|
|
26
|
+
properties: {
|
|
27
|
+
id: { type: 'number', description: 'Space ID' },
|
|
28
|
+
},
|
|
29
|
+
required: ['id'],
|
|
30
|
+
},
|
|
31
|
+
},
|
|
32
|
+
{
|
|
33
|
+
name: 'io_create_space',
|
|
34
|
+
description: 'Create a new iOffice space (room) on a floor.',
|
|
35
|
+
annotations: { readOnlyHint: false },
|
|
36
|
+
inputSchema: {
|
|
37
|
+
type: 'object',
|
|
38
|
+
properties: {
|
|
39
|
+
name: { type: 'string', description: 'Space name' },
|
|
40
|
+
floorId: { type: 'number', description: 'Floor ID this space belongs to' },
|
|
41
|
+
description: { type: 'string', description: 'Space description' },
|
|
42
|
+
capacity: { type: 'number', description: 'Maximum occupancy' },
|
|
43
|
+
squareFootage: { type: 'number', description: 'Square footage of the space' },
|
|
44
|
+
typeId: { type: 'number', description: 'Space type ID' },
|
|
45
|
+
},
|
|
46
|
+
required: ['name', 'floorId'],
|
|
47
|
+
},
|
|
48
|
+
},
|
|
49
|
+
{
|
|
50
|
+
name: 'io_update_space',
|
|
51
|
+
description: 'Update an existing iOffice space. Only provide fields to change.',
|
|
52
|
+
annotations: { readOnlyHint: false },
|
|
53
|
+
inputSchema: {
|
|
54
|
+
type: 'object',
|
|
55
|
+
properties: {
|
|
56
|
+
id: { type: 'number', description: 'Space ID' },
|
|
57
|
+
name: { type: 'string', description: 'Space name' },
|
|
58
|
+
description: { type: 'string', description: 'Space description' },
|
|
59
|
+
capacity: { type: 'number', description: 'Maximum occupancy' },
|
|
60
|
+
squareFootage: { type: 'number', description: 'Square footage' },
|
|
61
|
+
typeId: { type: 'number', description: 'Space type ID' },
|
|
62
|
+
},
|
|
63
|
+
required: ['id'],
|
|
64
|
+
},
|
|
65
|
+
},
|
|
66
|
+
{
|
|
67
|
+
name: 'io_delete_space',
|
|
68
|
+
description: 'Delete an iOffice space by ID.',
|
|
69
|
+
annotations: { readOnlyHint: false, destructiveHint: true },
|
|
70
|
+
inputSchema: {
|
|
71
|
+
type: 'object',
|
|
72
|
+
properties: {
|
|
73
|
+
id: { type: 'number', description: 'Space ID' },
|
|
74
|
+
},
|
|
75
|
+
required: ['id'],
|
|
76
|
+
},
|
|
77
|
+
},
|
|
78
|
+
];
|
|
79
|
+
export async function handleTool(name, args, client) {
|
|
80
|
+
switch (name) {
|
|
81
|
+
case 'io_list_spaces': {
|
|
82
|
+
const { floorId, search, limit, startAt, orderBy, orderByType } = args;
|
|
83
|
+
const qs = buildQueryString({ search, limit, startAt, orderBy, orderByType });
|
|
84
|
+
const path = floorId ? `/floors/${floorId}/spaces${qs}` : `/spaces${qs}`;
|
|
85
|
+
const data = await client.request('GET', path);
|
|
86
|
+
return { content: [{ type: 'text', text: JSON.stringify(data, null, 2) }] };
|
|
87
|
+
}
|
|
88
|
+
case 'io_get_space': {
|
|
89
|
+
const { id } = args;
|
|
90
|
+
const data = await client.request('GET', `/spaces/${id}`);
|
|
91
|
+
return { content: [{ type: 'text', text: JSON.stringify(data, null, 2) }] };
|
|
92
|
+
}
|
|
93
|
+
case 'io_create_space': {
|
|
94
|
+
const data = await client.request('POST', '/spaces', args);
|
|
95
|
+
return { content: [{ type: 'text', text: JSON.stringify(data, null, 2) }] };
|
|
96
|
+
}
|
|
97
|
+
case 'io_update_space': {
|
|
98
|
+
const { id, ...body } = args;
|
|
99
|
+
const data = await client.request('PUT', `/spaces/${id}`, body);
|
|
100
|
+
return { content: [{ type: 'text', text: JSON.stringify(data, null, 2) }] };
|
|
101
|
+
}
|
|
102
|
+
case 'io_delete_space': {
|
|
103
|
+
const { id } = args;
|
|
104
|
+
const data = await client.request('DELETE', `/spaces/${id}`);
|
|
105
|
+
return { content: [{ type: 'text', text: JSON.stringify(data, null, 2) }] };
|
|
106
|
+
}
|
|
107
|
+
default:
|
|
108
|
+
throw new Error(`Unknown tool: ${name}`);
|
|
109
|
+
}
|
|
110
|
+
}
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
import { buildQueryString } from '../client.js';
|
|
2
|
+
export const toolDefinitions = [
|
|
3
|
+
{
|
|
4
|
+
name: 'io_list_users',
|
|
5
|
+
description: 'List iOffice users. Supports search, pagination, and sorting.',
|
|
6
|
+
annotations: { readOnlyHint: true },
|
|
7
|
+
inputSchema: {
|
|
8
|
+
type: 'object',
|
|
9
|
+
properties: {
|
|
10
|
+
search: { type: 'string', description: 'Filter by name or email' },
|
|
11
|
+
limit: { type: 'number', description: 'Max results (default 50, max 100)' },
|
|
12
|
+
startAt: { type: 'number', description: 'Pagination offset (default 0)' },
|
|
13
|
+
orderBy: { type: 'string', description: 'Property to sort by (default: id)' },
|
|
14
|
+
orderByType: { type: 'string', enum: ['asc', 'desc'], description: 'Sort direction (default: asc)' },
|
|
15
|
+
},
|
|
16
|
+
required: [],
|
|
17
|
+
},
|
|
18
|
+
},
|
|
19
|
+
{
|
|
20
|
+
name: 'io_get_user',
|
|
21
|
+
description: 'Get a single iOffice user by ID.',
|
|
22
|
+
annotations: { readOnlyHint: true },
|
|
23
|
+
inputSchema: {
|
|
24
|
+
type: 'object',
|
|
25
|
+
properties: {
|
|
26
|
+
id: { type: 'number', description: 'User ID' },
|
|
27
|
+
},
|
|
28
|
+
required: ['id'],
|
|
29
|
+
},
|
|
30
|
+
},
|
|
31
|
+
{
|
|
32
|
+
name: 'io_create_user',
|
|
33
|
+
description: 'Create a new iOffice user.',
|
|
34
|
+
annotations: { readOnlyHint: false },
|
|
35
|
+
inputSchema: {
|
|
36
|
+
type: 'object',
|
|
37
|
+
properties: {
|
|
38
|
+
firstName: { type: 'string', description: 'First name' },
|
|
39
|
+
lastName: { type: 'string', description: 'Last name' },
|
|
40
|
+
email: { type: 'string', description: 'Email address (used for login)' },
|
|
41
|
+
username: { type: 'string', description: 'Username' },
|
|
42
|
+
phone: { type: 'string', description: 'Phone number' },
|
|
43
|
+
title: { type: 'string', description: 'Job title' },
|
|
44
|
+
centerId: { type: 'number', description: 'Primary center/cost center ID' },
|
|
45
|
+
buildingId: { type: 'number', description: 'Default building ID' },
|
|
46
|
+
},
|
|
47
|
+
required: ['firstName', 'lastName', 'email'],
|
|
48
|
+
},
|
|
49
|
+
},
|
|
50
|
+
{
|
|
51
|
+
name: 'io_update_user',
|
|
52
|
+
description: 'Update an existing iOffice user. Only provide fields to change.',
|
|
53
|
+
annotations: { readOnlyHint: false },
|
|
54
|
+
inputSchema: {
|
|
55
|
+
type: 'object',
|
|
56
|
+
properties: {
|
|
57
|
+
id: { type: 'number', description: 'User ID' },
|
|
58
|
+
firstName: { type: 'string', description: 'First name' },
|
|
59
|
+
lastName: { type: 'string', description: 'Last name' },
|
|
60
|
+
email: { type: 'string', description: 'Email address' },
|
|
61
|
+
phone: { type: 'string', description: 'Phone number' },
|
|
62
|
+
title: { type: 'string', description: 'Job title' },
|
|
63
|
+
centerId: { type: 'number', description: 'Primary center/cost center ID' },
|
|
64
|
+
buildingId: { type: 'number', description: 'Default building ID' },
|
|
65
|
+
},
|
|
66
|
+
required: ['id'],
|
|
67
|
+
},
|
|
68
|
+
},
|
|
69
|
+
{
|
|
70
|
+
name: 'io_delete_user',
|
|
71
|
+
description: 'Delete an iOffice user by ID.',
|
|
72
|
+
annotations: { readOnlyHint: false, destructiveHint: true },
|
|
73
|
+
inputSchema: {
|
|
74
|
+
type: 'object',
|
|
75
|
+
properties: {
|
|
76
|
+
id: { type: 'number', description: 'User ID' },
|
|
77
|
+
},
|
|
78
|
+
required: ['id'],
|
|
79
|
+
},
|
|
80
|
+
},
|
|
81
|
+
];
|
|
82
|
+
export async function handleTool(name, args, client) {
|
|
83
|
+
switch (name) {
|
|
84
|
+
case 'io_list_users': {
|
|
85
|
+
const { search, limit, startAt, orderBy, orderByType } = args;
|
|
86
|
+
const qs = buildQueryString({ search, limit, startAt, orderBy, orderByType });
|
|
87
|
+
const data = await client.request('GET', `/users${qs}`);
|
|
88
|
+
return { content: [{ type: 'text', text: JSON.stringify(data, null, 2) }] };
|
|
89
|
+
}
|
|
90
|
+
case 'io_get_user': {
|
|
91
|
+
const { id } = args;
|
|
92
|
+
const data = await client.request('GET', `/users/${id}`);
|
|
93
|
+
return { content: [{ type: 'text', text: JSON.stringify(data, null, 2) }] };
|
|
94
|
+
}
|
|
95
|
+
case 'io_create_user': {
|
|
96
|
+
const data = await client.request('POST', '/users', args);
|
|
97
|
+
return { content: [{ type: 'text', text: JSON.stringify(data, null, 2) }] };
|
|
98
|
+
}
|
|
99
|
+
case 'io_update_user': {
|
|
100
|
+
const { id, ...body } = args;
|
|
101
|
+
const data = await client.request('PUT', `/users/${id}`, body);
|
|
102
|
+
return { content: [{ type: 'text', text: JSON.stringify(data, null, 2) }] };
|
|
103
|
+
}
|
|
104
|
+
case 'io_delete_user': {
|
|
105
|
+
const { id } = args;
|
|
106
|
+
const data = await client.request('DELETE', `/users/${id}`);
|
|
107
|
+
return { content: [{ type: 'text', text: JSON.stringify(data, null, 2) }] };
|
|
108
|
+
}
|
|
109
|
+
default:
|
|
110
|
+
throw new Error(`Unknown tool: ${name}`);
|
|
111
|
+
}
|
|
112
|
+
}
|
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
import { buildQueryString } from '../client.js';
|
|
2
|
+
export const toolDefinitions = [
|
|
3
|
+
{
|
|
4
|
+
name: 'io_list_visitors',
|
|
5
|
+
description: 'List iOffice visitors. Supports search, date filtering, and pagination.',
|
|
6
|
+
annotations: { readOnlyHint: true },
|
|
7
|
+
inputSchema: {
|
|
8
|
+
type: 'object',
|
|
9
|
+
properties: {
|
|
10
|
+
search: { type: 'string', description: 'Filter by visitor name or email' },
|
|
11
|
+
startDate: { type: 'string', description: 'Filter visitors expected on or after this date (ISO 8601)' },
|
|
12
|
+
endDate: { type: 'string', description: 'Filter visitors expected on or before this date (ISO 8601)' },
|
|
13
|
+
buildingId: { type: 'number', description: 'Filter by building ID' },
|
|
14
|
+
limit: { type: 'number', description: 'Max results (default 50, max 100)' },
|
|
15
|
+
startAt: { type: 'number', description: 'Pagination offset (default 0)' },
|
|
16
|
+
orderBy: { type: 'string', description: 'Property to sort by (default: id)' },
|
|
17
|
+
orderByType: { type: 'string', enum: ['asc', 'desc'], description: 'Sort direction (default: asc)' },
|
|
18
|
+
},
|
|
19
|
+
required: [],
|
|
20
|
+
},
|
|
21
|
+
},
|
|
22
|
+
{
|
|
23
|
+
name: 'io_get_visitor',
|
|
24
|
+
description: 'Get a single iOffice visitor by ID.',
|
|
25
|
+
annotations: { readOnlyHint: true },
|
|
26
|
+
inputSchema: {
|
|
27
|
+
type: 'object',
|
|
28
|
+
properties: {
|
|
29
|
+
id: { type: 'number', description: 'Visitor ID' },
|
|
30
|
+
},
|
|
31
|
+
required: ['id'],
|
|
32
|
+
},
|
|
33
|
+
},
|
|
34
|
+
{
|
|
35
|
+
name: 'io_create_visitor',
|
|
36
|
+
description: 'Pre-register a visitor in iOffice.',
|
|
37
|
+
annotations: { readOnlyHint: false },
|
|
38
|
+
inputSchema: {
|
|
39
|
+
type: 'object',
|
|
40
|
+
properties: {
|
|
41
|
+
firstName: { type: 'string', description: 'Visitor first name' },
|
|
42
|
+
lastName: { type: 'string', description: 'Visitor last name' },
|
|
43
|
+
email: { type: 'string', description: 'Visitor email address' },
|
|
44
|
+
company: { type: 'string', description: 'Visitor company/organization' },
|
|
45
|
+
phone: { type: 'string', description: 'Visitor phone number' },
|
|
46
|
+
hostId: { type: 'number', description: 'Host user ID (iOffice user they are visiting)' },
|
|
47
|
+
buildingId: { type: 'number', description: 'Building ID for the visit' },
|
|
48
|
+
expectedArrival: { type: 'string', description: 'Expected arrival date/time (ISO 8601)' },
|
|
49
|
+
expectedDeparture: { type: 'string', description: 'Expected departure date/time (ISO 8601)' },
|
|
50
|
+
purpose: { type: 'string', description: 'Purpose of visit' },
|
|
51
|
+
},
|
|
52
|
+
required: ['firstName', 'lastName'],
|
|
53
|
+
},
|
|
54
|
+
},
|
|
55
|
+
{
|
|
56
|
+
name: 'io_update_visitor',
|
|
57
|
+
description: 'Update an existing iOffice visitor record. Only provide fields to change.',
|
|
58
|
+
annotations: { readOnlyHint: false },
|
|
59
|
+
inputSchema: {
|
|
60
|
+
type: 'object',
|
|
61
|
+
properties: {
|
|
62
|
+
id: { type: 'number', description: 'Visitor ID' },
|
|
63
|
+
firstName: { type: 'string', description: 'Visitor first name' },
|
|
64
|
+
lastName: { type: 'string', description: 'Visitor last name' },
|
|
65
|
+
email: { type: 'string', description: 'Visitor email address' },
|
|
66
|
+
company: { type: 'string', description: 'Visitor company/organization' },
|
|
67
|
+
phone: { type: 'string', description: 'Visitor phone number' },
|
|
68
|
+
expectedArrival: { type: 'string', description: 'Expected arrival date/time (ISO 8601)' },
|
|
69
|
+
expectedDeparture: { type: 'string', description: 'Expected departure date/time (ISO 8601)' },
|
|
70
|
+
purpose: { type: 'string', description: 'Purpose of visit' },
|
|
71
|
+
},
|
|
72
|
+
required: ['id'],
|
|
73
|
+
},
|
|
74
|
+
},
|
|
75
|
+
{
|
|
76
|
+
name: 'io_checkin_visitor',
|
|
77
|
+
description: 'Check in a visitor upon arrival at the building.',
|
|
78
|
+
annotations: { readOnlyHint: false },
|
|
79
|
+
inputSchema: {
|
|
80
|
+
type: 'object',
|
|
81
|
+
properties: {
|
|
82
|
+
id: { type: 'number', description: 'Visitor ID' },
|
|
83
|
+
},
|
|
84
|
+
required: ['id'],
|
|
85
|
+
},
|
|
86
|
+
},
|
|
87
|
+
{
|
|
88
|
+
name: 'io_checkout_visitor',
|
|
89
|
+
description: 'Check out a visitor upon departure from the building.',
|
|
90
|
+
annotations: { readOnlyHint: false },
|
|
91
|
+
inputSchema: {
|
|
92
|
+
type: 'object',
|
|
93
|
+
properties: {
|
|
94
|
+
id: { type: 'number', description: 'Visitor ID' },
|
|
95
|
+
},
|
|
96
|
+
required: ['id'],
|
|
97
|
+
},
|
|
98
|
+
},
|
|
99
|
+
];
|
|
100
|
+
export async function handleTool(name, args, client) {
|
|
101
|
+
switch (name) {
|
|
102
|
+
case 'io_list_visitors': {
|
|
103
|
+
const { search, startDate, endDate, buildingId, limit, startAt, orderBy, orderByType } = args;
|
|
104
|
+
const qs = buildQueryString({ search, startDate, endDate, buildingId, limit, startAt, orderBy, orderByType });
|
|
105
|
+
const data = await client.request('GET', `/visitors${qs}`);
|
|
106
|
+
return { content: [{ type: 'text', text: JSON.stringify(data, null, 2) }] };
|
|
107
|
+
}
|
|
108
|
+
case 'io_get_visitor': {
|
|
109
|
+
const { id } = args;
|
|
110
|
+
const data = await client.request('GET', `/visitors/${id}`);
|
|
111
|
+
return { content: [{ type: 'text', text: JSON.stringify(data, null, 2) }] };
|
|
112
|
+
}
|
|
113
|
+
case 'io_create_visitor': {
|
|
114
|
+
const data = await client.request('POST', '/visitors', args);
|
|
115
|
+
return { content: [{ type: 'text', text: JSON.stringify(data, null, 2) }] };
|
|
116
|
+
}
|
|
117
|
+
case 'io_update_visitor': {
|
|
118
|
+
const { id, ...body } = args;
|
|
119
|
+
const data = await client.request('PUT', `/visitors/${id}`, body);
|
|
120
|
+
return { content: [{ type: 'text', text: JSON.stringify(data, null, 2) }] };
|
|
121
|
+
}
|
|
122
|
+
case 'io_checkin_visitor': {
|
|
123
|
+
const { id } = args;
|
|
124
|
+
const data = await client.request('POST', `/visitors/${id}/checkIn`);
|
|
125
|
+
return { content: [{ type: 'text', text: JSON.stringify(data, null, 2) }] };
|
|
126
|
+
}
|
|
127
|
+
case 'io_checkout_visitor': {
|
|
128
|
+
const { id } = args;
|
|
129
|
+
const data = await client.request('POST', `/visitors/${id}/checkOut`);
|
|
130
|
+
return { content: [{ type: 'text', text: JSON.stringify(data, null, 2) }] };
|
|
131
|
+
}
|
|
132
|
+
default:
|
|
133
|
+
throw new Error(`Unknown tool: ${name}`);
|
|
134
|
+
}
|
|
135
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "ioffice-mcp",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "iOffice MCP server for Claude — developed and maintained by AI (Claude Sonnet 4.6)",
|
|
5
|
+
"author": "Claude Sonnet 4.6 (AI) <https://www.anthropic.com/claude>",
|
|
6
|
+
"repository": {
|
|
7
|
+
"type": "git",
|
|
8
|
+
"url": "https://github.com/chrischall/ioffice-mcp"
|
|
9
|
+
},
|
|
10
|
+
"type": "module",
|
|
11
|
+
"bin": {
|
|
12
|
+
"ioffice-mcp": "dist/index.js"
|
|
13
|
+
},
|
|
14
|
+
"files": [
|
|
15
|
+
"dist"
|
|
16
|
+
],
|
|
17
|
+
"scripts": {
|
|
18
|
+
"build": "tsc",
|
|
19
|
+
"dev": "node --env-file=.env dist/index.js",
|
|
20
|
+
"test": "vitest run",
|
|
21
|
+
"test:watch": "vitest",
|
|
22
|
+
"test:coverage": "vitest run --coverage"
|
|
23
|
+
},
|
|
24
|
+
"dependencies": {
|
|
25
|
+
"@modelcontextprotocol/sdk": "^1.27.1",
|
|
26
|
+
"dotenv": "^17.3.1"
|
|
27
|
+
},
|
|
28
|
+
"devDependencies": {
|
|
29
|
+
"@types/node": "^25.5.0",
|
|
30
|
+
"@vitest/coverage-v8": "^4.1.0",
|
|
31
|
+
"typescript": "^5.9.3",
|
|
32
|
+
"vitest": "^4.1.0"
|
|
33
|
+
}
|
|
34
|
+
}
|