acuity-mcp-server 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/README.md +541 -0
- package/dist/auth/device-flow.d.ts +46 -0
- package/dist/auth/device-flow.d.ts.map +1 -0
- package/dist/auth/device-flow.js +141 -0
- package/dist/auth/device-flow.js.map +1 -0
- package/dist/auth/http-auth.d.ts +25 -0
- package/dist/auth/http-auth.d.ts.map +1 -0
- package/dist/auth/http-auth.js +101 -0
- package/dist/auth/http-auth.js.map +1 -0
- package/dist/auth/jwt-validator.d.ts +20 -0
- package/dist/auth/jwt-validator.d.ts.map +1 -0
- package/dist/auth/jwt-validator.js +83 -0
- package/dist/auth/jwt-validator.js.map +1 -0
- package/dist/auth/token-storage.d.ts +88 -0
- package/dist/auth/token-storage.d.ts.map +1 -0
- package/dist/auth/token-storage.js +273 -0
- package/dist/auth/token-storage.js.map +1 -0
- package/dist/clients/hasura-client.d.ts +33 -0
- package/dist/clients/hasura-client.d.ts.map +1 -0
- package/dist/clients/hasura-client.js +79 -0
- package/dist/clients/hasura-client.js.map +1 -0
- package/dist/config/environments.d.ts +51 -0
- package/dist/config/environments.d.ts.map +1 -0
- package/dist/config/environments.js +183 -0
- package/dist/config/environments.js.map +1 -0
- package/dist/index.d.ts +7 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +593 -0
- package/dist/index.js.map +1 -0
- package/dist/server/http-server.d.ts +14 -0
- package/dist/server/http-server.d.ts.map +1 -0
- package/dist/server/http-server.js +167 -0
- package/dist/server/http-server.js.map +1 -0
- package/dist/server/mcp-core.d.ts +12 -0
- package/dist/server/mcp-core.d.ts.map +1 -0
- package/dist/server/mcp-core.js +200 -0
- package/dist/server/mcp-core.js.map +1 -0
- package/dist/tools/acuity-init.d.ts +84 -0
- package/dist/tools/acuity-init.d.ts.map +1 -0
- package/dist/tools/acuity-init.js +239 -0
- package/dist/tools/acuity-init.js.map +1 -0
- package/dist/tools/get-dashboard-summary.d.ts +96 -0
- package/dist/tools/get-dashboard-summary.d.ts.map +1 -0
- package/dist/tools/get-dashboard-summary.js +264 -0
- package/dist/tools/get-dashboard-summary.js.map +1 -0
- package/dist/tools/get-issue.d.ts +62 -0
- package/dist/tools/get-issue.d.ts.map +1 -0
- package/dist/tools/get-issue.js +150 -0
- package/dist/tools/get-issue.js.map +1 -0
- package/dist/tools/get-lesson-learned.d.ts +53 -0
- package/dist/tools/get-lesson-learned.d.ts.map +1 -0
- package/dist/tools/get-lesson-learned.js +117 -0
- package/dist/tools/get-lesson-learned.js.map +1 -0
- package/dist/tools/get-lookup-values.d.ts +41 -0
- package/dist/tools/get-lookup-values.d.ts.map +1 -0
- package/dist/tools/get-lookup-values.js +127 -0
- package/dist/tools/get-lookup-values.js.map +1 -0
- package/dist/tools/get-project.d.ts +131 -0
- package/dist/tools/get-project.d.ts.map +1 -0
- package/dist/tools/get-project.js +340 -0
- package/dist/tools/get-project.js.map +1 -0
- package/dist/tools/get-risk.d.ts +65 -0
- package/dist/tools/get-risk.d.ts.map +1 -0
- package/dist/tools/get-risk.js +173 -0
- package/dist/tools/get-risk.js.map +1 -0
- package/dist/tools/get-status-reports.d.ts +46 -0
- package/dist/tools/get-status-reports.d.ts.map +1 -0
- package/dist/tools/get-status-reports.js +151 -0
- package/dist/tools/get-status-reports.js.map +1 -0
- package/dist/tools/init-auth.d.ts +47 -0
- package/dist/tools/init-auth.d.ts.map +1 -0
- package/dist/tools/init-auth.js +213 -0
- package/dist/tools/init-auth.js.map +1 -0
- package/dist/tools/list-issues.d.ts +134 -0
- package/dist/tools/list-issues.d.ts.map +1 -0
- package/dist/tools/list-issues.js +285 -0
- package/dist/tools/list-issues.js.map +1 -0
- package/dist/tools/list-lessons-learned.d.ts +79 -0
- package/dist/tools/list-lessons-learned.d.ts.map +1 -0
- package/dist/tools/list-lessons-learned.js +155 -0
- package/dist/tools/list-lessons-learned.js.map +1 -0
- package/dist/tools/list-projects.d.ts +200 -0
- package/dist/tools/list-projects.d.ts.map +1 -0
- package/dist/tools/list-projects.js +396 -0
- package/dist/tools/list-projects.js.map +1 -0
- package/dist/tools/list-risks.d.ts +166 -0
- package/dist/tools/list-risks.d.ts.map +1 -0
- package/dist/tools/list-risks.js +356 -0
- package/dist/tools/list-risks.js.map +1 -0
- package/dist/tools/search-projects.d.ts +90 -0
- package/dist/tools/search-projects.d.ts.map +1 -0
- package/dist/tools/search-projects.js +191 -0
- package/dist/tools/search-projects.js.map +1 -0
- package/dist/utils/formatters.d.ts +12 -0
- package/dist/utils/formatters.d.ts.map +1 -0
- package/dist/utils/formatters.js +28 -0
- package/dist/utils/formatters.js.map +1 -0
- package/openapi.yaml +194 -0
- package/package.json +68 -0
|
@@ -0,0 +1,191 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* MCP Tool: search_projects
|
|
3
|
+
* Search projects by name or description using text matching
|
|
4
|
+
*
|
|
5
|
+
* Uses PostgreSQL ILIKE for case-insensitive partial matching
|
|
6
|
+
*/
|
|
7
|
+
// ============================================================================
|
|
8
|
+
// Enum Mappings
|
|
9
|
+
// ============================================================================
|
|
10
|
+
const STATE_TO_DB = {
|
|
11
|
+
active: 0,
|
|
12
|
+
hold: 1,
|
|
13
|
+
completed: 2,
|
|
14
|
+
cancelled: 3,
|
|
15
|
+
pending: 4
|
|
16
|
+
};
|
|
17
|
+
const DB_TO_STATE = {
|
|
18
|
+
0: 'active',
|
|
19
|
+
1: 'hold',
|
|
20
|
+
2: 'completed',
|
|
21
|
+
3: 'cancelled',
|
|
22
|
+
4: 'pending'
|
|
23
|
+
};
|
|
24
|
+
const DB_TO_STOPLIGHT = {
|
|
25
|
+
0: 'green',
|
|
26
|
+
1: 'yellow',
|
|
27
|
+
2: 'red',
|
|
28
|
+
3: 'gray'
|
|
29
|
+
};
|
|
30
|
+
// ============================================================================
|
|
31
|
+
// Helper Functions
|
|
32
|
+
// ============================================================================
|
|
33
|
+
function fromDbValue(value, mapping) {
|
|
34
|
+
if (value === null || value === undefined)
|
|
35
|
+
return null;
|
|
36
|
+
return mapping[value] ?? null;
|
|
37
|
+
}
|
|
38
|
+
function transformProject(raw) {
|
|
39
|
+
return {
|
|
40
|
+
id: raw.id,
|
|
41
|
+
project_id_key: raw.project_id_key,
|
|
42
|
+
name: raw.name,
|
|
43
|
+
description: raw.description,
|
|
44
|
+
project_type: raw.project_type,
|
|
45
|
+
state: fromDbValue(raw.state, DB_TO_STATE) ?? 'active',
|
|
46
|
+
health_status: fromDbValue(raw.health_status, DB_TO_STOPLIGHT),
|
|
47
|
+
percent_complete: raw.percent_complete ?? 0,
|
|
48
|
+
portfolio: raw.portfolio,
|
|
49
|
+
department: raw.department
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
// ============================================================================
|
|
53
|
+
// Main Handler
|
|
54
|
+
// ============================================================================
|
|
55
|
+
export async function searchProjects(input, hasuraClient) {
|
|
56
|
+
const { query: searchQuery, search_in = ['name', 'description', 'project_id_key'], project_type = 'project', state, limit = 20 } = input;
|
|
57
|
+
// Build search conditions using _ilike (case-insensitive)
|
|
58
|
+
const searchPattern = `%${searchQuery}%`;
|
|
59
|
+
const searchConditions = [];
|
|
60
|
+
if (search_in.includes('name')) {
|
|
61
|
+
searchConditions.push({ name: { _ilike: searchPattern } });
|
|
62
|
+
}
|
|
63
|
+
if (search_in.includes('description')) {
|
|
64
|
+
searchConditions.push({ description: { _ilike: searchPattern } });
|
|
65
|
+
}
|
|
66
|
+
if (search_in.includes('project_id_key')) {
|
|
67
|
+
searchConditions.push({ project_id_key: { _ilike: searchPattern } });
|
|
68
|
+
}
|
|
69
|
+
// Build where clause
|
|
70
|
+
const whereConditions = {
|
|
71
|
+
_or: searchConditions
|
|
72
|
+
};
|
|
73
|
+
// Filter by project_type (default: 'project', use 'all' to search both)
|
|
74
|
+
if (project_type !== 'all') {
|
|
75
|
+
whereConditions.project_type = { _eq: project_type };
|
|
76
|
+
}
|
|
77
|
+
// Add state filter if provided
|
|
78
|
+
if (state !== undefined) {
|
|
79
|
+
const stateArr = Array.isArray(state) ? state : [state];
|
|
80
|
+
const stateValues = stateArr.map(s => STATE_TO_DB[s]).filter(v => v !== undefined);
|
|
81
|
+
if (stateValues.length > 0) {
|
|
82
|
+
whereConditions.state = stateValues.length === 1
|
|
83
|
+
? { _eq: stateValues[0] }
|
|
84
|
+
: { _in: stateValues };
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
// Build GraphQL query
|
|
88
|
+
const whereClause = JSON.stringify(whereConditions).replace(/"([^"]+)":/g, '$1:');
|
|
89
|
+
const query = `
|
|
90
|
+
query SearchProjects($limit: Int!) {
|
|
91
|
+
projects(
|
|
92
|
+
where: ${whereClause},
|
|
93
|
+
order_by: { updated_at: desc },
|
|
94
|
+
limit: $limit
|
|
95
|
+
) {
|
|
96
|
+
id
|
|
97
|
+
project_id_key
|
|
98
|
+
name
|
|
99
|
+
description
|
|
100
|
+
project_type
|
|
101
|
+
state
|
|
102
|
+
health_status
|
|
103
|
+
percent_complete
|
|
104
|
+
portfolio { id name }
|
|
105
|
+
department { id name }
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
projects_aggregate(where: ${whereClause}) {
|
|
109
|
+
aggregate {
|
|
110
|
+
count
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
`;
|
|
115
|
+
const result = await hasuraClient.query(query, { limit });
|
|
116
|
+
return {
|
|
117
|
+
projects: result.projects.map(transformProject),
|
|
118
|
+
total_count: result.projects_aggregate.aggregate.count,
|
|
119
|
+
query: searchQuery
|
|
120
|
+
};
|
|
121
|
+
}
|
|
122
|
+
// ============================================================================
|
|
123
|
+
// Tool Definition
|
|
124
|
+
// ============================================================================
|
|
125
|
+
export const searchProjectsTool = {
|
|
126
|
+
name: 'search_projects',
|
|
127
|
+
description: `
|
|
128
|
+
Search for projects OR proposals by name, description, or project ID key.
|
|
129
|
+
|
|
130
|
+
Uses case-insensitive partial matching (e.g., "infra" matches "Infrastructure Upgrade").
|
|
131
|
+
|
|
132
|
+
PARAMETERS:
|
|
133
|
+
- query (required): Search text to find in project/proposal fields
|
|
134
|
+
- search_in (optional): Which fields to search in. Default: ["name", "description", "project_id_key"]
|
|
135
|
+
- "name" - Project/proposal name
|
|
136
|
+
- "description" - Project/proposal description
|
|
137
|
+
- "project_id_key" - Human-readable ID (e.g., "PRJ-001")
|
|
138
|
+
- project_type (optional): "project" (default), "proposal", or "all"
|
|
139
|
+
- state (optional): Filter by state
|
|
140
|
+
- limit (optional): Max results (default: 20)
|
|
141
|
+
|
|
142
|
+
RETURNS (for each match):
|
|
143
|
+
- id, project_id_key, name, description, project_type
|
|
144
|
+
- state, health_status, percent_complete
|
|
145
|
+
- portfolio, department (with names)
|
|
146
|
+
|
|
147
|
+
EXAMPLES:
|
|
148
|
+
- "Find projects about infrastructure" → query: "infrastructure"
|
|
149
|
+
- "Search proposals for 'mobile'" → query: "mobile", project_type: "proposal"
|
|
150
|
+
- "Search for PRJ-001" → query: "PRJ-001"
|
|
151
|
+
- "Find active projects with 'mobile' in the name" → query: "mobile", search_in: ["name"], state: "active"
|
|
152
|
+
- "Search everything for 'upgrade'" → query: "upgrade", project_type: "all"
|
|
153
|
+
`.trim(),
|
|
154
|
+
inputSchema: {
|
|
155
|
+
type: 'object',
|
|
156
|
+
properties: {
|
|
157
|
+
query: {
|
|
158
|
+
type: 'string',
|
|
159
|
+
description: 'Search text (case-insensitive, partial match)'
|
|
160
|
+
},
|
|
161
|
+
search_in: {
|
|
162
|
+
type: 'array',
|
|
163
|
+
items: {
|
|
164
|
+
type: 'string',
|
|
165
|
+
enum: ['name', 'description', 'project_id_key']
|
|
166
|
+
},
|
|
167
|
+
description: 'Fields to search in. Default: all three fields.'
|
|
168
|
+
},
|
|
169
|
+
project_type: {
|
|
170
|
+
type: 'string',
|
|
171
|
+
enum: ['project', 'proposal', 'all'],
|
|
172
|
+
description: 'Search in projects (default), proposals, or all',
|
|
173
|
+
default: 'project'
|
|
174
|
+
},
|
|
175
|
+
state: {
|
|
176
|
+
oneOf: [
|
|
177
|
+
{ type: 'string', enum: ['active', 'hold', 'completed', 'cancelled', 'pending'] },
|
|
178
|
+
{ type: 'array', items: { type: 'string', enum: ['active', 'hold', 'completed', 'cancelled', 'pending'] } }
|
|
179
|
+
],
|
|
180
|
+
description: 'Optional: filter results by state'
|
|
181
|
+
},
|
|
182
|
+
limit: {
|
|
183
|
+
type: 'number',
|
|
184
|
+
description: 'Maximum results to return (default: 20)',
|
|
185
|
+
default: 20
|
|
186
|
+
}
|
|
187
|
+
},
|
|
188
|
+
required: ['query']
|
|
189
|
+
}
|
|
190
|
+
};
|
|
191
|
+
//# sourceMappingURL=search-projects.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"search-projects.js","sourceRoot":"","sources":["../../src/tools/search-projects.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Formatting utilities for LLM-friendly responses
|
|
3
|
+
*/
|
|
4
|
+
/**
|
|
5
|
+
* Format date string to readable format
|
|
6
|
+
*/
|
|
7
|
+
export declare function formatDate(dateString: string | null | undefined): string;
|
|
8
|
+
/**
|
|
9
|
+
* Create a divider line
|
|
10
|
+
*/
|
|
11
|
+
export declare function divider(length?: number): string;
|
|
12
|
+
//# sourceMappingURL=formatters.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"formatters.d.ts","sourceRoot":"","sources":["../../src/utils/formatters.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH;;GAEG;AACH,wBAAgB,UAAU,CAAC,UAAU,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,GAAG,MAAM,CAcxE;AAED;;GAEG;AACH,wBAAgB,OAAO,CAAC,MAAM,GAAE,MAAW,GAAG,MAAM,CAEnD"}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Formatting utilities for LLM-friendly responses
|
|
3
|
+
*/
|
|
4
|
+
/**
|
|
5
|
+
* Format date string to readable format
|
|
6
|
+
*/
|
|
7
|
+
export function formatDate(dateString) {
|
|
8
|
+
if (!dateString) {
|
|
9
|
+
return 'N/A';
|
|
10
|
+
}
|
|
11
|
+
try {
|
|
12
|
+
return new Date(dateString).toLocaleDateString('en-US', {
|
|
13
|
+
year: 'numeric',
|
|
14
|
+
month: 'long',
|
|
15
|
+
day: 'numeric'
|
|
16
|
+
});
|
|
17
|
+
}
|
|
18
|
+
catch {
|
|
19
|
+
return dateString;
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Create a divider line
|
|
24
|
+
*/
|
|
25
|
+
export function divider(length = 60) {
|
|
26
|
+
return '='.repeat(length);
|
|
27
|
+
}
|
|
28
|
+
//# sourceMappingURL=formatters.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"formatters.js","sourceRoot":"","sources":["../../src/utils/formatters.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH;;GAEG;AACH,MAAM,UAAU,UAAU,CAAC,UAAqC;IAC9D,IAAI,CAAC,UAAU,EAAE,CAAC;QAChB,OAAO,KAAK,CAAC;IACf,CAAC;IAED,IAAI,CAAC;QACH,OAAO,IAAI,IAAI,CAAC,UAAU,CAAC,CAAC,kBAAkB,CAAC,OAAO,EAAE;YACtD,IAAI,EAAE,SAAS;YACf,KAAK,EAAE,MAAM;YACb,GAAG,EAAE,SAAS;SACf,CAAC,CAAC;IACL,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,UAAU,CAAC;IACpB,CAAC;AACH,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,OAAO,CAAC,SAAiB,EAAE;IACzC,OAAO,GAAG,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC;AAC5B,CAAC"}
|
package/openapi.yaml
ADDED
|
@@ -0,0 +1,194 @@
|
|
|
1
|
+
swagger: '2.0'
|
|
2
|
+
info:
|
|
3
|
+
title: Acuity MCP Server
|
|
4
|
+
description: |
|
|
5
|
+
AI-powered access to Acuity Project Management data via Model Context Protocol.
|
|
6
|
+
|
|
7
|
+
This MCP server provides tools for:
|
|
8
|
+
- Listing and searching projects
|
|
9
|
+
- Viewing project details, risks, issues, and lessons learned
|
|
10
|
+
- Generating status reports and dashboard summaries
|
|
11
|
+
- Accessing lookup values for departments, categories, etc.
|
|
12
|
+
|
|
13
|
+
## Authentication
|
|
14
|
+
All requests require OAuth 2.0 Bearer token authentication.
|
|
15
|
+
Obtain tokens from Auth0 (acuityppm.auth0.com).
|
|
16
|
+
version: 1.0.0
|
|
17
|
+
contact:
|
|
18
|
+
name: Acuity PPM
|
|
19
|
+
url: https://acuityppm.com
|
|
20
|
+
host: YOUR_AZURE_APP_URL.azurecontainerapps.io
|
|
21
|
+
basePath: /
|
|
22
|
+
schemes:
|
|
23
|
+
- https
|
|
24
|
+
securityDefinitions:
|
|
25
|
+
oauth2:
|
|
26
|
+
type: oauth2
|
|
27
|
+
flow: implicit
|
|
28
|
+
authorizationUrl: https://acuityppm.auth0.com/authorize
|
|
29
|
+
scopes:
|
|
30
|
+
openid: OpenID Connect
|
|
31
|
+
profile: User profile
|
|
32
|
+
email: User email
|
|
33
|
+
apiKey:
|
|
34
|
+
type: apiKey
|
|
35
|
+
name: x-api-key
|
|
36
|
+
in: header
|
|
37
|
+
description: API key for service-to-service authentication
|
|
38
|
+
paths:
|
|
39
|
+
/mcp:
|
|
40
|
+
post:
|
|
41
|
+
summary: Acuity Project Management MCP Server
|
|
42
|
+
description: |
|
|
43
|
+
Model Context Protocol endpoint for AI-powered project management access.
|
|
44
|
+
|
|
45
|
+
Provides tools for:
|
|
46
|
+
- acuity_init: Initialize connection and get company context
|
|
47
|
+
- list_projects: List projects with filtering
|
|
48
|
+
- get_project: Get detailed project information
|
|
49
|
+
- search_projects: Search projects by keyword
|
|
50
|
+
- get_status_reports: Get project status reports
|
|
51
|
+
- list_risks / get_risk: Access project risks
|
|
52
|
+
- list_issues / get_issue: Access project issues
|
|
53
|
+
- list_lessons_learned / get_lesson_learned: Access lessons learned
|
|
54
|
+
- get_lookup_values: Get department, category, lifecycle values
|
|
55
|
+
- get_dashboard_summary: Get portfolio dashboard data
|
|
56
|
+
operationId: InvokeMCP
|
|
57
|
+
x-ms-agentic-protocol: mcp-streamable-1.0
|
|
58
|
+
consumes:
|
|
59
|
+
- application/json
|
|
60
|
+
produces:
|
|
61
|
+
- application/json
|
|
62
|
+
- text/event-stream
|
|
63
|
+
security:
|
|
64
|
+
- oauth2:
|
|
65
|
+
- openid
|
|
66
|
+
- profile
|
|
67
|
+
- email
|
|
68
|
+
- apiKey: []
|
|
69
|
+
parameters:
|
|
70
|
+
- name: mcp-session-id
|
|
71
|
+
in: header
|
|
72
|
+
type: string
|
|
73
|
+
required: false
|
|
74
|
+
description: MCP session identifier (returned after initialization)
|
|
75
|
+
- name: body
|
|
76
|
+
in: body
|
|
77
|
+
required: true
|
|
78
|
+
schema:
|
|
79
|
+
type: object
|
|
80
|
+
description: JSON-RPC 2.0 request
|
|
81
|
+
responses:
|
|
82
|
+
'200':
|
|
83
|
+
description: Successful MCP response
|
|
84
|
+
schema:
|
|
85
|
+
type: object
|
|
86
|
+
'202':
|
|
87
|
+
description: Accepted (for notifications/responses without reply)
|
|
88
|
+
'400':
|
|
89
|
+
description: Bad request (invalid session or request format)
|
|
90
|
+
schema:
|
|
91
|
+
$ref: '#/definitions/JsonRpcError'
|
|
92
|
+
'401':
|
|
93
|
+
description: Authentication required or token invalid
|
|
94
|
+
schema:
|
|
95
|
+
$ref: '#/definitions/JsonRpcError'
|
|
96
|
+
'500':
|
|
97
|
+
description: Internal server error
|
|
98
|
+
schema:
|
|
99
|
+
$ref: '#/definitions/JsonRpcError'
|
|
100
|
+
get:
|
|
101
|
+
summary: MCP Server-Sent Events stream
|
|
102
|
+
description: |
|
|
103
|
+
Opens an SSE stream for receiving server-initiated messages.
|
|
104
|
+
Requires an active session (mcp-session-id header).
|
|
105
|
+
operationId: GetMCPStream
|
|
106
|
+
x-ms-agentic-protocol: mcp-streamable-1.0
|
|
107
|
+
produces:
|
|
108
|
+
- text/event-stream
|
|
109
|
+
security:
|
|
110
|
+
- oauth2:
|
|
111
|
+
- openid
|
|
112
|
+
- profile
|
|
113
|
+
- email
|
|
114
|
+
- apiKey: []
|
|
115
|
+
parameters:
|
|
116
|
+
- name: mcp-session-id
|
|
117
|
+
in: header
|
|
118
|
+
type: string
|
|
119
|
+
required: true
|
|
120
|
+
description: MCP session identifier
|
|
121
|
+
responses:
|
|
122
|
+
'200':
|
|
123
|
+
description: SSE stream opened
|
|
124
|
+
'400':
|
|
125
|
+
description: Invalid or missing session
|
|
126
|
+
schema:
|
|
127
|
+
$ref: '#/definitions/JsonRpcError'
|
|
128
|
+
'405':
|
|
129
|
+
description: SSE streams not supported
|
|
130
|
+
delete:
|
|
131
|
+
summary: Close MCP session
|
|
132
|
+
description: Terminates an active MCP session and releases resources.
|
|
133
|
+
operationId: CloseMCPSession
|
|
134
|
+
x-ms-agentic-protocol: mcp-streamable-1.0
|
|
135
|
+
security:
|
|
136
|
+
- oauth2:
|
|
137
|
+
- openid
|
|
138
|
+
- profile
|
|
139
|
+
- email
|
|
140
|
+
- apiKey: []
|
|
141
|
+
parameters:
|
|
142
|
+
- name: mcp-session-id
|
|
143
|
+
in: header
|
|
144
|
+
type: string
|
|
145
|
+
required: true
|
|
146
|
+
description: MCP session identifier to close
|
|
147
|
+
responses:
|
|
148
|
+
'200':
|
|
149
|
+
description: Session closed successfully
|
|
150
|
+
'400':
|
|
151
|
+
description: Invalid or missing session
|
|
152
|
+
schema:
|
|
153
|
+
$ref: '#/definitions/JsonRpcError'
|
|
154
|
+
/health:
|
|
155
|
+
get:
|
|
156
|
+
summary: Health check
|
|
157
|
+
description: Returns server health status
|
|
158
|
+
operationId: HealthCheck
|
|
159
|
+
produces:
|
|
160
|
+
- application/json
|
|
161
|
+
responses:
|
|
162
|
+
'200':
|
|
163
|
+
description: Server is healthy
|
|
164
|
+
schema:
|
|
165
|
+
type: object
|
|
166
|
+
properties:
|
|
167
|
+
status:
|
|
168
|
+
type: string
|
|
169
|
+
example: ok
|
|
170
|
+
service:
|
|
171
|
+
type: string
|
|
172
|
+
example: acuity-mcp-server
|
|
173
|
+
activeSessions:
|
|
174
|
+
type: integer
|
|
175
|
+
example: 5
|
|
176
|
+
definitions:
|
|
177
|
+
JsonRpcError:
|
|
178
|
+
type: object
|
|
179
|
+
properties:
|
|
180
|
+
jsonrpc:
|
|
181
|
+
type: string
|
|
182
|
+
example: '2.0'
|
|
183
|
+
error:
|
|
184
|
+
type: object
|
|
185
|
+
properties:
|
|
186
|
+
code:
|
|
187
|
+
type: integer
|
|
188
|
+
example: -32000
|
|
189
|
+
message:
|
|
190
|
+
type: string
|
|
191
|
+
example: Invalid session
|
|
192
|
+
id:
|
|
193
|
+
type: string
|
|
194
|
+
nullable: true
|
package/package.json
ADDED
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "acuity-mcp-server",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "MCP server for Acuity Project Management - enables LLM access to project data",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "dist/index.js",
|
|
7
|
+
"bin": {
|
|
8
|
+
"acuity-mcp-server": "./dist/index.js"
|
|
9
|
+
},
|
|
10
|
+
"files": [
|
|
11
|
+
"dist",
|
|
12
|
+
"!dist/**/__tests__",
|
|
13
|
+
"openapi.yaml",
|
|
14
|
+
"README.md"
|
|
15
|
+
],
|
|
16
|
+
"repository": {
|
|
17
|
+
"type": "git",
|
|
18
|
+
"url": "https://github.com/AcuityPPM/acuity-mcp-server"
|
|
19
|
+
},
|
|
20
|
+
"homepage": "https://acuityppm.com",
|
|
21
|
+
"scripts": {
|
|
22
|
+
"dev": "tsx watch src/index.ts",
|
|
23
|
+
"build": "tsc",
|
|
24
|
+
"start": "node dist/index.js",
|
|
25
|
+
"typecheck": "tsc --noEmit",
|
|
26
|
+
"test": "NODE_OPTIONS='--experimental-vm-modules' jest",
|
|
27
|
+
"test:watch": "NODE_OPTIONS='--experimental-vm-modules' jest --watch",
|
|
28
|
+
"test:coverage": "NODE_OPTIONS='--experimental-vm-modules' jest --coverage"
|
|
29
|
+
},
|
|
30
|
+
"keywords": [
|
|
31
|
+
"mcp",
|
|
32
|
+
"model-context-protocol",
|
|
33
|
+
"acuity",
|
|
34
|
+
"project-management",
|
|
35
|
+
"claude",
|
|
36
|
+
"copilot",
|
|
37
|
+
"ai",
|
|
38
|
+
"llm"
|
|
39
|
+
],
|
|
40
|
+
"author": "Acuity PPM",
|
|
41
|
+
"license": "ISC",
|
|
42
|
+
"dependencies": {
|
|
43
|
+
"@modelcontextprotocol/sdk": "^1.21.1",
|
|
44
|
+
"dotenv": "^17.0.0",
|
|
45
|
+
"express": "^5.2.1",
|
|
46
|
+
"graphql": "^16.12.0",
|
|
47
|
+
"graphql-request": "^7.3.3",
|
|
48
|
+
"jsonwebtoken": "^9.0.2",
|
|
49
|
+
"jwks-rsa": "^3.2.0",
|
|
50
|
+
"open": "^11.0.0"
|
|
51
|
+
},
|
|
52
|
+
"optionalDependencies": {
|
|
53
|
+
"keytar": "^7.9.0"
|
|
54
|
+
},
|
|
55
|
+
"devDependencies": {
|
|
56
|
+
"@types/express": "^5.0.6",
|
|
57
|
+
"@types/jest": "^30.0.0",
|
|
58
|
+
"@types/jsonwebtoken": "^9.0.10",
|
|
59
|
+
"@types/keytar": "^4.4.0",
|
|
60
|
+
"@types/nock": "^10.0.3",
|
|
61
|
+
"@types/node": "^24.10.0",
|
|
62
|
+
"jest": "^30.2.0",
|
|
63
|
+
"nock": "^14.0.10",
|
|
64
|
+
"ts-jest": "^29.4.6",
|
|
65
|
+
"tsx": "^4.20.6",
|
|
66
|
+
"typescript": "^5.9.3"
|
|
67
|
+
}
|
|
68
|
+
}
|