@snokam/mcp-server 0.2.0 → 0.3.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/index.js +97 -2
- package/dist/openapi-loader.d.ts +1 -0
- package/dist/openapi-loader.js +2 -0
- package/package.json +1 -1
- package/specs/test/broker.json +109 -0
package/dist/index.js
CHANGED
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
*/
|
|
9
9
|
import { Server } from "@modelcontextprotocol/sdk/server/index.js";
|
|
10
10
|
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
11
|
-
import { CallToolRequestSchema, ListToolsRequestSchema, } from "@modelcontextprotocol/sdk/types.js";
|
|
11
|
+
import { CallToolRequestSchema, ListToolsRequestSchema, ListResourcesRequestSchema, ReadResourceRequestSchema, } from "@modelcontextprotocol/sdk/types.js";
|
|
12
12
|
import { fetchSpecs } from "./openapi-loader.js";
|
|
13
13
|
import { getAccessToken } from "./auth.js";
|
|
14
14
|
// ---------------------------------------------------------------------------
|
|
@@ -120,7 +120,102 @@ async function main() {
|
|
|
120
120
|
for (const ep of endpoints) {
|
|
121
121
|
endpointsByTool.set(ep.toolName, ep);
|
|
122
122
|
}
|
|
123
|
-
const server = new Server({
|
|
123
|
+
const server = new Server({
|
|
124
|
+
name: "snokam",
|
|
125
|
+
version: "0.2.0",
|
|
126
|
+
}, {
|
|
127
|
+
capabilities: {
|
|
128
|
+
tools: {},
|
|
129
|
+
resources: {},
|
|
130
|
+
},
|
|
131
|
+
});
|
|
132
|
+
// List resources
|
|
133
|
+
server.setRequestHandler(ListResourcesRequestSchema, async () => ({
|
|
134
|
+
resources: [
|
|
135
|
+
{
|
|
136
|
+
uri: "snokam://about",
|
|
137
|
+
name: "About Snøkam",
|
|
138
|
+
description: "Information about Snøkam and available API services",
|
|
139
|
+
mimeType: "text/markdown",
|
|
140
|
+
},
|
|
141
|
+
],
|
|
142
|
+
}));
|
|
143
|
+
// Read resource
|
|
144
|
+
server.setRequestHandler(ReadResourceRequestSchema, async (request) => {
|
|
145
|
+
const { uri } = request.params;
|
|
146
|
+
if (uri === "snokam://about") {
|
|
147
|
+
// Group endpoints by service with their descriptions
|
|
148
|
+
const serviceMap = new Map();
|
|
149
|
+
for (const ep of endpoints) {
|
|
150
|
+
const existing = serviceMap.get(ep.service);
|
|
151
|
+
if (existing) {
|
|
152
|
+
existing.count++;
|
|
153
|
+
}
|
|
154
|
+
else {
|
|
155
|
+
serviceMap.set(ep.service, {
|
|
156
|
+
count: 1,
|
|
157
|
+
description: ep.serviceDescription,
|
|
158
|
+
});
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
const serviceList = Array.from(serviceMap.entries())
|
|
162
|
+
.sort(([a], [b]) => a.localeCompare(b))
|
|
163
|
+
.map(([service, { count, description }]) => {
|
|
164
|
+
return `- **${service}** (${count} endpoints): ${description}`;
|
|
165
|
+
})
|
|
166
|
+
.join("\n");
|
|
167
|
+
const about = `# Snøkam MCP Server
|
|
168
|
+
|
|
169
|
+
**Snøkam** is a Norwegian software consulting company. This MCP server provides programmatic access to Snøkam's internal backend APIs.
|
|
170
|
+
|
|
171
|
+
## Environment
|
|
172
|
+
|
|
173
|
+
Currently connected to: **${ENVIRONMENT}**
|
|
174
|
+
|
|
175
|
+
## Available Services
|
|
176
|
+
|
|
177
|
+
${serviceList}
|
|
178
|
+
|
|
179
|
+
## Authentication
|
|
180
|
+
|
|
181
|
+
- **Public endpoints**: No authentication required (e.g., \`employees__GetEmployeesPublic\`)
|
|
182
|
+
- **Protected endpoints**: Require Azure AD authentication (e.g., \`employees__GetEmployeesProtected\`)
|
|
183
|
+
|
|
184
|
+
## Common Use Cases
|
|
185
|
+
|
|
186
|
+
**Find out who works at Snøkam:**
|
|
187
|
+
\`\`\`
|
|
188
|
+
Use: employees__GetEmployeesPublic
|
|
189
|
+
Returns: List of all employees with names, roles, and technologies
|
|
190
|
+
\`\`\`
|
|
191
|
+
|
|
192
|
+
**Get upcoming events:**
|
|
193
|
+
\`\`\`
|
|
194
|
+
Use: events__GetPublicEvents (if available)
|
|
195
|
+
Returns: Company events and gatherings
|
|
196
|
+
\`\`\`
|
|
197
|
+
|
|
198
|
+
**Check office status or control music:**
|
|
199
|
+
\`\`\`
|
|
200
|
+
Use: office__* tools
|
|
201
|
+
Controls: Sonos speakers, lights, YouTube queue
|
|
202
|
+
\`\`\`
|
|
203
|
+
`;
|
|
204
|
+
return {
|
|
205
|
+
contents: [
|
|
206
|
+
{
|
|
207
|
+
uri,
|
|
208
|
+
mimeType: "text/markdown",
|
|
209
|
+
text: about,
|
|
210
|
+
},
|
|
211
|
+
],
|
|
212
|
+
};
|
|
213
|
+
}
|
|
214
|
+
return {
|
|
215
|
+
contents: [],
|
|
216
|
+
isError: true,
|
|
217
|
+
};
|
|
218
|
+
});
|
|
124
219
|
// List tools
|
|
125
220
|
server.setRequestHandler(ListToolsRequestSchema, async () => ({
|
|
126
221
|
tools: endpoints.map((ep) => ({
|
package/dist/openapi-loader.d.ts
CHANGED
package/dist/openapi-loader.js
CHANGED
|
@@ -70,6 +70,7 @@ function parseSpec(spec, service, baseUrl) {
|
|
|
70
70
|
const paths = spec.paths;
|
|
71
71
|
if (!paths || Object.keys(paths).length === 0)
|
|
72
72
|
return endpoints;
|
|
73
|
+
const serviceDescription = spec.info.description || spec.info.title || "";
|
|
73
74
|
for (const [path, pathItem] of Object.entries(paths)) {
|
|
74
75
|
for (const method of ["get", "post", "put", "patch", "delete"]) {
|
|
75
76
|
const operation = pathItem[method];
|
|
@@ -81,6 +82,7 @@ function parseSpec(spec, service, baseUrl) {
|
|
|
81
82
|
const scope = extractScope(operation);
|
|
82
83
|
endpoints.push({
|
|
83
84
|
service,
|
|
85
|
+
serviceDescription,
|
|
84
86
|
toolName: makeToolName(service, operationId),
|
|
85
87
|
operationId,
|
|
86
88
|
method: method.toUpperCase(),
|
package/package.json
CHANGED
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
{
|
|
2
|
+
"openapi": "3.0.1",
|
|
3
|
+
"info": {
|
|
4
|
+
"title": "Broker Function",
|
|
5
|
+
"description": "Message broker for Sanity events (Webhook) to Azure Service Bus / PubSub",
|
|
6
|
+
"version": "v1.0.0"
|
|
7
|
+
},
|
|
8
|
+
"servers": [
|
|
9
|
+
{
|
|
10
|
+
"url": "https://broker.api.test.snokam.no"
|
|
11
|
+
}
|
|
12
|
+
],
|
|
13
|
+
"paths": {
|
|
14
|
+
"/v1.0/sanity-webhook-to-azure": {
|
|
15
|
+
"post": {
|
|
16
|
+
"tags": [
|
|
17
|
+
"Sanity"
|
|
18
|
+
],
|
|
19
|
+
"summary": "Handles Sanity webhook events",
|
|
20
|
+
"description": "This function processes events from Sanity webhooks and forwards them to Azure Web PubSub and Service Bus.",
|
|
21
|
+
"operationId": "SanityWebhookToAzure",
|
|
22
|
+
"requestBody": {
|
|
23
|
+
"description": "The Sanity webhook payload",
|
|
24
|
+
"content": {
|
|
25
|
+
"application/json": {
|
|
26
|
+
"schema": {
|
|
27
|
+
"$ref": "#/components/schemas/sanityDocument"
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
},
|
|
31
|
+
"required": true
|
|
32
|
+
},
|
|
33
|
+
"responses": {
|
|
34
|
+
"200": {
|
|
35
|
+
"description": "Event processed successfully",
|
|
36
|
+
"x-ms-summary": "Success"
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
},
|
|
41
|
+
"/v1.0/pubsub/tv/uri": {
|
|
42
|
+
"get": {
|
|
43
|
+
"tags": [
|
|
44
|
+
"WebPubSub"
|
|
45
|
+
],
|
|
46
|
+
"summary": "Gets a Web PubSub token",
|
|
47
|
+
"description": "Generates a Web PubSub token for joining and leaving the 'tv' group.",
|
|
48
|
+
"operationId": "GetPubsubToken",
|
|
49
|
+
"responses": {
|
|
50
|
+
"200": {
|
|
51
|
+
"description": "Web PubSub token generated",
|
|
52
|
+
"content": {
|
|
53
|
+
"application/json": {
|
|
54
|
+
"schema": {
|
|
55
|
+
"$ref": "#/components/schemas/pubSubToken"
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
},
|
|
59
|
+
"x-ms-summary": "Success"
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
},
|
|
65
|
+
"components": {
|
|
66
|
+
"schemas": {
|
|
67
|
+
"pubSubToken": {
|
|
68
|
+
"type": "object",
|
|
69
|
+
"properties": {
|
|
70
|
+
"uri": {
|
|
71
|
+
"type": "string",
|
|
72
|
+
"nullable": true
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
},
|
|
76
|
+
"sanityDocument": {
|
|
77
|
+
"type": "object",
|
|
78
|
+
"properties": {
|
|
79
|
+
"_id": {
|
|
80
|
+
"type": "string",
|
|
81
|
+
"nullable": true
|
|
82
|
+
},
|
|
83
|
+
"_type": {
|
|
84
|
+
"type": "string",
|
|
85
|
+
"nullable": true
|
|
86
|
+
},
|
|
87
|
+
"_rev": {
|
|
88
|
+
"type": "string",
|
|
89
|
+
"nullable": true
|
|
90
|
+
},
|
|
91
|
+
"_key": {
|
|
92
|
+
"type": "string",
|
|
93
|
+
"nullable": true
|
|
94
|
+
},
|
|
95
|
+
"_createdAt": {
|
|
96
|
+
"type": "string",
|
|
97
|
+
"format": "date-time",
|
|
98
|
+
"nullable": true
|
|
99
|
+
},
|
|
100
|
+
"_updatedAt": {
|
|
101
|
+
"type": "string",
|
|
102
|
+
"format": "date-time",
|
|
103
|
+
"nullable": true
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
}
|