@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 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({ name: "snokam", version: "0.1.0" }, { capabilities: { tools: {} } });
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) => ({
@@ -8,6 +8,7 @@
8
8
  */
9
9
  export interface ApiEndpoint {
10
10
  service: string;
11
+ serviceDescription: string;
11
12
  toolName: string;
12
13
  operationId: string;
13
14
  method: string;
@@ -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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@snokam/mcp-server",
3
- "version": "0.2.0",
3
+ "version": "0.3.0",
4
4
  "description": "MCP server exposing Snokam backend APIs as tools",
5
5
  "type": "module",
6
6
  "bin": {
@@ -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
+ }