@interface-db/mcp 1.0.67
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/LICENSE.md +202 -0
- package/NOTICE +4 -0
- package/README.md +105 -0
- package/dist/crypto.d.ts +16 -0
- package/dist/crypto.d.ts.map +1 -0
- package/dist/crypto.js +45 -0
- package/dist/crypto.js.map +1 -0
- package/dist/db/instant.perms.d.ts +9 -0
- package/dist/db/instant.perms.d.ts.map +1 -0
- package/dist/db/instant.perms.js +10 -0
- package/dist/db/instant.perms.js.map +1 -0
- package/dist/db/instant.schema.d.ts +162 -0
- package/dist/db/instant.schema.d.ts.map +1 -0
- package/dist/db/instant.schema.js +167 -0
- package/dist/db/instant.schema.js.map +1 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.html.d.ts +3 -0
- package/dist/index.html.d.ts.map +1 -0
- package/dist/index.html.js +111 -0
- package/dist/index.html.js.map +1 -0
- package/dist/index.js +456 -0
- package/dist/index.js.map +1 -0
- package/dist/oauth-service-provider.d.ts +37 -0
- package/dist/oauth-service-provider.d.ts.map +1 -0
- package/dist/oauth-service-provider.js +603 -0
- package/dist/oauth-service-provider.js.map +1 -0
- package/dist/schema.d.ts +29 -0
- package/dist/schema.d.ts.map +1 -0
- package/dist/schema.js +52 -0
- package/dist/schema.js.map +1 -0
- package/dist/tools.d.ts +12 -0
- package/dist/tools.d.ts.map +1 -0
- package/dist/tools.js +72 -0
- package/dist/tools.js.map +1 -0
- package/dist/version.d.ts +3 -0
- package/dist/version.d.ts.map +1 -0
- package/dist/version.js +3 -0
- package/dist/version.js.map +1 -0
- package/package.json +68 -0
package/dist/index.js
ADDED
|
@@ -0,0 +1,456 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import 'dotenv/config';
|
|
3
|
+
import express from 'express';
|
|
4
|
+
import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
|
|
5
|
+
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
|
|
6
|
+
import { z } from 'zod';
|
|
7
|
+
import { parseArgs } from 'node:util';
|
|
8
|
+
import version from "./version.js";
|
|
9
|
+
import { StreamableHTTPServerTransport } from '@modelcontextprotocol/sdk/server/streamableHttp.js';
|
|
10
|
+
import { HoneycombSDK } from '@honeycombio/opentelemetry-node';
|
|
11
|
+
import { trace, SpanKind, SpanStatusCode, } from '@opentelemetry/api';
|
|
12
|
+
import { createOAuthMetadata, mcpAuthRouter, } from '@modelcontextprotocol/sdk/server/auth/router.js';
|
|
13
|
+
import { pinoHttp } from 'pino-http';
|
|
14
|
+
import { pino } from 'pino';
|
|
15
|
+
import { init } from '@interface-db/admin';
|
|
16
|
+
import schema from "./db/instant.schema.js";
|
|
17
|
+
import { requireBearerAuth } from '@modelcontextprotocol/sdk/server/auth/middleware/bearerAuth.js';
|
|
18
|
+
import { addOAuthRoutes, makeApiAuth, ServiceProvider, tokensOfBearerToken, } from "./oauth-service-provider.js";
|
|
19
|
+
import { PlatformApi } from '@interface-db/platform';
|
|
20
|
+
import indexHtml from "./index.html.js";
|
|
21
|
+
import { SSEServerTransport } from '@modelcontextprotocol/sdk/server/sse.js';
|
|
22
|
+
import { handleQuery, handleTransact } from "./tools.js";
|
|
23
|
+
// Helpers
|
|
24
|
+
// -----------
|
|
25
|
+
function createMCPServer() {
|
|
26
|
+
return new McpServer({
|
|
27
|
+
name: '@interface-db/mcp',
|
|
28
|
+
version,
|
|
29
|
+
});
|
|
30
|
+
}
|
|
31
|
+
// Tool Registration
|
|
32
|
+
// -----------
|
|
33
|
+
// Adds tracing to server.tool
|
|
34
|
+
function wrapServerWithTracing(server, tracer, attrs) {
|
|
35
|
+
const originalTool = server.tool.bind(server);
|
|
36
|
+
server.tool = function (name, ...args) {
|
|
37
|
+
// Find the callback (it's always the last argument)
|
|
38
|
+
const callback = args[args.length - 1];
|
|
39
|
+
const otherArgs = args.slice(0, -1);
|
|
40
|
+
// Wrap the callback with tracing
|
|
41
|
+
const wrappedCallback = async (...callbackArgs) => {
|
|
42
|
+
const span = tracer.startSpan(`tool.${name}`, {
|
|
43
|
+
attributes: attrs,
|
|
44
|
+
});
|
|
45
|
+
try {
|
|
46
|
+
const result = await callback(...callbackArgs);
|
|
47
|
+
span.setStatus({ code: SpanStatusCode.OK });
|
|
48
|
+
return result;
|
|
49
|
+
}
|
|
50
|
+
catch (error) {
|
|
51
|
+
span.setStatus({ code: SpanStatusCode.ERROR });
|
|
52
|
+
span.recordException(error);
|
|
53
|
+
throw error;
|
|
54
|
+
}
|
|
55
|
+
finally {
|
|
56
|
+
span.end();
|
|
57
|
+
}
|
|
58
|
+
};
|
|
59
|
+
return originalTool(name,
|
|
60
|
+
// @ts-expect-error: not sure how to type this
|
|
61
|
+
...otherArgs, wrappedCallback);
|
|
62
|
+
};
|
|
63
|
+
return server;
|
|
64
|
+
}
|
|
65
|
+
function registerTools(server, api) {
|
|
66
|
+
server.tool('learn', "If you don't have any context provided about InstantDB, use this tool to learn about it!", {}, async () => {
|
|
67
|
+
const instructions = `
|
|
68
|
+
You can learn about InstantDB by fetching our rules file for agents:
|
|
69
|
+
|
|
70
|
+
https://www.interfacedb.com/llm-rules/AGENTS.md
|
|
71
|
+
`;
|
|
72
|
+
return {
|
|
73
|
+
content: [
|
|
74
|
+
{
|
|
75
|
+
type: 'text',
|
|
76
|
+
text: instructions,
|
|
77
|
+
},
|
|
78
|
+
],
|
|
79
|
+
};
|
|
80
|
+
});
|
|
81
|
+
server.tool('get-schema', 'Fetch schema for an app by its ID!', {
|
|
82
|
+
appId: z.string().uuid().describe('UUID of the app'),
|
|
83
|
+
}, async ({ appId }) => {
|
|
84
|
+
const instructions = `
|
|
85
|
+
You can fetch the schema for the app by using the instant-cli tool:
|
|
86
|
+
|
|
87
|
+
\`\`\`
|
|
88
|
+
npx @interface-db/cli pull schema --app ${appId} --yes
|
|
89
|
+
\`\`\`
|
|
90
|
+
|
|
91
|
+
We supply the --yes flag to skip confirmation prompts. Now 'instant.schema.ts' will contain the schema for the app.
|
|
92
|
+
`;
|
|
93
|
+
return {
|
|
94
|
+
content: [
|
|
95
|
+
{
|
|
96
|
+
type: 'text',
|
|
97
|
+
text: instructions,
|
|
98
|
+
},
|
|
99
|
+
],
|
|
100
|
+
};
|
|
101
|
+
});
|
|
102
|
+
server.tool('get-perms', 'Fetch permissions for an app by its ID', {
|
|
103
|
+
appId: z.string().uuid().describe('UUID of the app'),
|
|
104
|
+
}, async ({ appId }) => {
|
|
105
|
+
const instructions = `
|
|
106
|
+
You fetch the permissions for the app by using the instant-cli tool:
|
|
107
|
+
|
|
108
|
+
\`\`\`
|
|
109
|
+
npx @interface-db/cli pull perms --app ${appId} --yes
|
|
110
|
+
\`\`\`
|
|
111
|
+
|
|
112
|
+
We supply the --yes flag to skip confirmation prompts. Now 'instant.perms.ts' will contain the permissions for the app.
|
|
113
|
+
`;
|
|
114
|
+
return {
|
|
115
|
+
content: [
|
|
116
|
+
{
|
|
117
|
+
type: 'text',
|
|
118
|
+
text: instructions,
|
|
119
|
+
},
|
|
120
|
+
],
|
|
121
|
+
};
|
|
122
|
+
});
|
|
123
|
+
server.tool('push-schema', `Push local schema changes for an app to the server. Do this after updating your local 'instant.schema.ts' file.
|
|
124
|
+
If you don't have an instant.schema.ts file yet, use the get-schema tool to learn how to get this file.`, {
|
|
125
|
+
appId: z.string().uuid().describe('UUID of the app'),
|
|
126
|
+
}, async ({ appId }) => {
|
|
127
|
+
const instructions = `
|
|
128
|
+
Push schema changes by using the instant-cli tool:
|
|
129
|
+
|
|
130
|
+
\`\`\`
|
|
131
|
+
npx @interface-db/cli push schema --app ${appId} --yes
|
|
132
|
+
\`\`\`
|
|
133
|
+
|
|
134
|
+
We supply the --yes flag to skip confirmation prompts.
|
|
135
|
+
|
|
136
|
+
By default the instant-cli tool will assume new fields from the previous schema are additions and missing fields are deletions.
|
|
137
|
+
If you want to rename fields as part of your schema changes you can use the --rename flag to specify renames.
|
|
138
|
+
|
|
139
|
+
\`\`\`
|
|
140
|
+
npx @interface-db/cli push schema --app ${appId} --rename 'posts.author:posts.creator stores.owner:stores.manager' --yes
|
|
141
|
+
\`\`\`
|
|
142
|
+
`;
|
|
143
|
+
return {
|
|
144
|
+
content: [
|
|
145
|
+
{
|
|
146
|
+
type: 'text',
|
|
147
|
+
text: instructions,
|
|
148
|
+
},
|
|
149
|
+
],
|
|
150
|
+
};
|
|
151
|
+
});
|
|
152
|
+
server.tool('push-perms', `Push local permissions changes for an app to the server. Do this after updating your local instant.perms.ts file.
|
|
153
|
+
If you don't have an instant.perms.ts file yet, use the get-perms tool to learn how to get this file.`, {
|
|
154
|
+
appId: z.string().uuid().describe('UUID of the app'),
|
|
155
|
+
}, async ({ appId }) => {
|
|
156
|
+
const instructions = `
|
|
157
|
+
Push permission changes by using the instant-cli tool:
|
|
158
|
+
|
|
159
|
+
\`\`\`
|
|
160
|
+
npx @interface-db/cli push perms --app ${appId} --yes
|
|
161
|
+
\`\`\`
|
|
162
|
+
|
|
163
|
+
We supply the --yes flag to skip confirmation prompts.
|
|
164
|
+
`;
|
|
165
|
+
return {
|
|
166
|
+
content: [
|
|
167
|
+
{
|
|
168
|
+
type: 'text',
|
|
169
|
+
text: instructions,
|
|
170
|
+
},
|
|
171
|
+
],
|
|
172
|
+
};
|
|
173
|
+
});
|
|
174
|
+
server.tool('query', `Execute an InstaQL query against an app. Returns the query results as JSON.
|
|
175
|
+
|
|
176
|
+
Example query to fetch all goals and their todos:
|
|
177
|
+
{"goals": {"todos": {}}}
|
|
178
|
+
|
|
179
|
+
Example query with a where clause:
|
|
180
|
+
{"goals": {"$": {"where": {"status": "active"}}, "todos": {}}}
|
|
181
|
+
|
|
182
|
+
If you're unsure how to write queries, refer to the documentation:
|
|
183
|
+
https://interfacedb.com/docs/instaql`, {
|
|
184
|
+
appId: z.string().uuid().describe('UUID of the app'),
|
|
185
|
+
query: z.record(z.string(), z.any()).describe('InstaQL query object'),
|
|
186
|
+
}, async ({ appId, query }) => {
|
|
187
|
+
return handleQuery(api, appId, query);
|
|
188
|
+
});
|
|
189
|
+
server.tool('transact', `Execute a transaction against an app. Useful for creating, updating, or deleting data.
|
|
190
|
+
|
|
191
|
+
Steps use the internal transaction format:
|
|
192
|
+
- Create/update: ["update", "namespace", "entity-id", {"attr": "value"}]
|
|
193
|
+
- Link: ["link", "namespace", "entity-id", {"linkAttr": "target-id"}]
|
|
194
|
+
- Unlink: ["unlink", "namespace", "entity-id", {"linkAttr": "target-id"}]
|
|
195
|
+
- Delete: ["delete", "namespace", "entity-id"]
|
|
196
|
+
|
|
197
|
+
Example steps to create a todo:
|
|
198
|
+
[["update", "todos", "a-uuid", {"title": "Get fit", "done": false}]]
|
|
199
|
+
|
|
200
|
+
If you're unsure how to make transactions, refer to the documentation:
|
|
201
|
+
https://interfacedb.com/docs/instaml`, {
|
|
202
|
+
appId: z.string().uuid().describe('UUID of the app'),
|
|
203
|
+
steps: z.array(z.array(z.any())).describe('Array of transaction steps'),
|
|
204
|
+
}, async ({ appId, steps }) => {
|
|
205
|
+
return handleTransact(api, appId, steps);
|
|
206
|
+
});
|
|
207
|
+
}
|
|
208
|
+
async function startStdio() {
|
|
209
|
+
const { values: { token, ['api-url']: apiUrl }, } = parseArgs({
|
|
210
|
+
options: {
|
|
211
|
+
token: {
|
|
212
|
+
type: 'string',
|
|
213
|
+
},
|
|
214
|
+
['api-url']: {
|
|
215
|
+
type: 'string',
|
|
216
|
+
},
|
|
217
|
+
},
|
|
218
|
+
});
|
|
219
|
+
const accessToken = token || process.env.INSTANT_ACCESS_TOKEN;
|
|
220
|
+
if (!accessToken) {
|
|
221
|
+
console.error('Provide an access token using --token or set INSTANT_ACCESS_TOKEN environment variable');
|
|
222
|
+
process.exit(1);
|
|
223
|
+
}
|
|
224
|
+
const api = new PlatformApi({ auth: { token: accessToken } });
|
|
225
|
+
const server = createMCPServer();
|
|
226
|
+
registerTools(server, api);
|
|
227
|
+
const transport = new StdioServerTransport();
|
|
228
|
+
await server.connect(transport);
|
|
229
|
+
console.error('Instant Platform MCP Server running on stdio');
|
|
230
|
+
}
|
|
231
|
+
function ensureEnv(key) {
|
|
232
|
+
const v = process.env[key];
|
|
233
|
+
if (!v) {
|
|
234
|
+
throw new Error(`Missing environment variable ${key}`);
|
|
235
|
+
}
|
|
236
|
+
return v;
|
|
237
|
+
}
|
|
238
|
+
async function startSse() {
|
|
239
|
+
const honeycomb = new HoneycombSDK({
|
|
240
|
+
apiKey: process.env.HONEYCOMB_API_KEY,
|
|
241
|
+
serviceName: 'mcp-server',
|
|
242
|
+
});
|
|
243
|
+
if (process.env.HONEYCOMB_API_KEY) {
|
|
244
|
+
honeycomb.start();
|
|
245
|
+
}
|
|
246
|
+
const tracer = trace.getTracer('mcp-server');
|
|
247
|
+
const db = init({
|
|
248
|
+
adminToken: ensureEnv('INSTANT_ADMIN_TOKEN'),
|
|
249
|
+
appId: ensureEnv('INSTANT_APP_ID'),
|
|
250
|
+
schema,
|
|
251
|
+
disableValidation: true,
|
|
252
|
+
});
|
|
253
|
+
const oauthConfig = {
|
|
254
|
+
clientId: ensureEnv('INSTANT_OAUTH_CLIENT_ID'),
|
|
255
|
+
clientSecret: ensureEnv('INSTANT_OAUTH_CLIENT_SECRET'),
|
|
256
|
+
serverOrigin: ensureEnv('SERVER_ORIGIN'),
|
|
257
|
+
};
|
|
258
|
+
const keyConfig = JSON.parse(ensureEnv('INSTANT_AES_KEY'));
|
|
259
|
+
const app = express();
|
|
260
|
+
const logger = pino({ level: 'info' });
|
|
261
|
+
app.use((req, res, next) => {
|
|
262
|
+
const span = tracer.startSpan('http-req', {
|
|
263
|
+
kind: SpanKind.SERVER,
|
|
264
|
+
attributes: {
|
|
265
|
+
'http.method': req.method,
|
|
266
|
+
'http.url': req.url,
|
|
267
|
+
'http.target': req.path,
|
|
268
|
+
'http.host': req.get('host'),
|
|
269
|
+
'http.scheme': req.protocol,
|
|
270
|
+
},
|
|
271
|
+
});
|
|
272
|
+
const originalEnd = res.end.bind(res);
|
|
273
|
+
res.end = function (...args) {
|
|
274
|
+
span.setAttribute('http.status_code', res.statusCode);
|
|
275
|
+
span.setStatus({
|
|
276
|
+
code: res.statusCode >= 400 ? SpanStatusCode.ERROR : SpanStatusCode.OK,
|
|
277
|
+
});
|
|
278
|
+
span.end();
|
|
279
|
+
return originalEnd(...args);
|
|
280
|
+
};
|
|
281
|
+
next();
|
|
282
|
+
});
|
|
283
|
+
app.use(pinoHttp({
|
|
284
|
+
logger,
|
|
285
|
+
autoLogging: {
|
|
286
|
+
ignore(req) {
|
|
287
|
+
return req.url === '/health';
|
|
288
|
+
},
|
|
289
|
+
},
|
|
290
|
+
}));
|
|
291
|
+
app.use(express.json());
|
|
292
|
+
const proxyProvider = new ServiceProvider(db, oauthConfig, keyConfig);
|
|
293
|
+
const authRouterOptions = {
|
|
294
|
+
scopesSupported: ['apps-read', 'apps-write'],
|
|
295
|
+
provider: proxyProvider,
|
|
296
|
+
issuerUrl: new URL(oauthConfig.serverOrigin),
|
|
297
|
+
baseUrl: new URL(oauthConfig.serverOrigin),
|
|
298
|
+
serviceDocumentationUrl: new URL('https://interfacedb.com/docs'),
|
|
299
|
+
};
|
|
300
|
+
const oauthMetadata = createOAuthMetadata(authRouterOptions);
|
|
301
|
+
app.use(mcpAuthRouter(authRouterOptions));
|
|
302
|
+
addOAuthRoutes(app, db, oauthConfig);
|
|
303
|
+
app.get('/.well-known/oauth-protected-resource/mcp', (_req, res) => {
|
|
304
|
+
res.json({
|
|
305
|
+
resource: `${oauthConfig.serverOrigin}/mcp`,
|
|
306
|
+
authorization_servers: [oauthMetadata.issuer],
|
|
307
|
+
scopes_supported: oauthMetadata.scopes_supported,
|
|
308
|
+
resource_documentation: 'https://interfacedb.com/docs',
|
|
309
|
+
});
|
|
310
|
+
});
|
|
311
|
+
app.get('/.well-known/oauth-protected-resource/sse', (_req, res) => {
|
|
312
|
+
res.json({
|
|
313
|
+
resource: `${oauthConfig.serverOrigin}/mcp`,
|
|
314
|
+
authorization_servers: [oauthMetadata.issuer],
|
|
315
|
+
scopes_supported: oauthMetadata.scopes_supported,
|
|
316
|
+
resource_documentation: 'https://interfacedb.com/docs',
|
|
317
|
+
});
|
|
318
|
+
});
|
|
319
|
+
const requireTokenMiddleware = (path) => requireBearerAuth({
|
|
320
|
+
verifier: proxyProvider,
|
|
321
|
+
resourceMetadataUrl: `${oauthConfig.serverOrigin}/.well-known/oauth-protected-resource/${path}`,
|
|
322
|
+
});
|
|
323
|
+
// Handle POST requests for client-to-server communication
|
|
324
|
+
app.post('/mcp', requireTokenMiddleware('mcp'), async (req, res) => {
|
|
325
|
+
const server = createMCPServer();
|
|
326
|
+
try {
|
|
327
|
+
const tokens = await tokensOfBearerToken(db, req.auth.token);
|
|
328
|
+
const api = new PlatformApi({
|
|
329
|
+
auth: makeApiAuth(oauthConfig, keyConfig, db, tokens.instantToken),
|
|
330
|
+
});
|
|
331
|
+
wrapServerWithTracing(server, tracer, {
|
|
332
|
+
'client.client_id': tokens.mcpToken.client?.client_id,
|
|
333
|
+
'client.name': tokens.mcpToken.client?.client_name,
|
|
334
|
+
'client.id': tokens.mcpToken.client?.id,
|
|
335
|
+
'client.scope': tokens.mcpToken.client?.scope,
|
|
336
|
+
'client.uri': tokens.mcpToken.client?.client_uri,
|
|
337
|
+
'client.redirect_urls': tokens.mcpToken.client?.redirect_uris,
|
|
338
|
+
});
|
|
339
|
+
registerTools(server, api);
|
|
340
|
+
const transport = new StreamableHTTPServerTransport({
|
|
341
|
+
sessionIdGenerator: undefined,
|
|
342
|
+
});
|
|
343
|
+
req.on('close', () => {
|
|
344
|
+
transport.close();
|
|
345
|
+
server.close();
|
|
346
|
+
});
|
|
347
|
+
await server.connect(transport);
|
|
348
|
+
await transport.handleRequest(req, res, req.body);
|
|
349
|
+
}
|
|
350
|
+
catch (e) {
|
|
351
|
+
console.error('Error handling MCP request:', e);
|
|
352
|
+
if (!res.headersSent) {
|
|
353
|
+
res.status(500).json({
|
|
354
|
+
jsonrpc: '2.0',
|
|
355
|
+
error: {
|
|
356
|
+
code: -32603,
|
|
357
|
+
message: 'Internal server error',
|
|
358
|
+
},
|
|
359
|
+
id: null,
|
|
360
|
+
});
|
|
361
|
+
}
|
|
362
|
+
}
|
|
363
|
+
});
|
|
364
|
+
// We're a stateless server, so disallow these
|
|
365
|
+
const handleSessionRequest = async (_req, res) => {
|
|
366
|
+
res.writeHead(405).end(JSON.stringify({
|
|
367
|
+
jsonrpc: '2.0',
|
|
368
|
+
error: {
|
|
369
|
+
code: -32000,
|
|
370
|
+
message: 'Method not allowed.',
|
|
371
|
+
},
|
|
372
|
+
id: null,
|
|
373
|
+
}));
|
|
374
|
+
};
|
|
375
|
+
app.get('/mcp', handleSessionRequest);
|
|
376
|
+
app.delete('/mcp', handleSessionRequest);
|
|
377
|
+
// SSE for older clients
|
|
378
|
+
const transports = {
|
|
379
|
+
sse: {},
|
|
380
|
+
};
|
|
381
|
+
app.get('/sse', requireTokenMiddleware('sse'), async (req, res) => {
|
|
382
|
+
const server = createMCPServer();
|
|
383
|
+
const transport = new SSEServerTransport('/messages', res);
|
|
384
|
+
res.on('close', () => {
|
|
385
|
+
delete transports.sse[transport.sessionId];
|
|
386
|
+
});
|
|
387
|
+
try {
|
|
388
|
+
const tokens = await tokensOfBearerToken(db, req.auth.token);
|
|
389
|
+
const api = new PlatformApi({
|
|
390
|
+
auth: makeApiAuth(oauthConfig, keyConfig, db, tokens.instantToken),
|
|
391
|
+
});
|
|
392
|
+
wrapServerWithTracing(server, tracer, {
|
|
393
|
+
'client.client_id': tokens.mcpToken.client?.client_id,
|
|
394
|
+
'client.name': tokens.mcpToken.client?.client_name,
|
|
395
|
+
'client.id': tokens.mcpToken.client?.id,
|
|
396
|
+
'client.scope': tokens.mcpToken.client?.scope,
|
|
397
|
+
'client.uri': tokens.mcpToken.client?.client_uri,
|
|
398
|
+
'client.redirect_urls': tokens.mcpToken.client?.redirect_uris,
|
|
399
|
+
});
|
|
400
|
+
registerTools(server, api);
|
|
401
|
+
transports.sse[transport.sessionId] = transport;
|
|
402
|
+
}
|
|
403
|
+
catch (e) {
|
|
404
|
+
console.error('Error handling MCP SSE request:', e);
|
|
405
|
+
if (!res.headersSent) {
|
|
406
|
+
res.status(500).json({
|
|
407
|
+
jsonrpc: '2.0',
|
|
408
|
+
error: {
|
|
409
|
+
code: -32603,
|
|
410
|
+
message: 'Internal server error',
|
|
411
|
+
},
|
|
412
|
+
id: null,
|
|
413
|
+
});
|
|
414
|
+
}
|
|
415
|
+
return;
|
|
416
|
+
}
|
|
417
|
+
await server.connect(transport);
|
|
418
|
+
});
|
|
419
|
+
// Legacy message endpoint for older clients
|
|
420
|
+
app.post('/messages', async (req, res) => {
|
|
421
|
+
const sessionId = req.query.sessionId;
|
|
422
|
+
const transport = transports.sse[sessionId];
|
|
423
|
+
if (transport) {
|
|
424
|
+
await transport.handlePostMessage(req, res, req.body);
|
|
425
|
+
}
|
|
426
|
+
else {
|
|
427
|
+
res.status(400).send('No transport found for sessionId');
|
|
428
|
+
}
|
|
429
|
+
});
|
|
430
|
+
app.get('/', (_req, res) => {
|
|
431
|
+
res
|
|
432
|
+
.status(200)
|
|
433
|
+
.set('Content-Type', 'text/html; charset=UTF-8')
|
|
434
|
+
.send(indexHtml(oauthConfig.serverOrigin));
|
|
435
|
+
});
|
|
436
|
+
app.get('/health', (_req, res) => {
|
|
437
|
+
res.status(200).send('Tip top!');
|
|
438
|
+
});
|
|
439
|
+
const port = parseInt(process.env.PORT || '3123');
|
|
440
|
+
const host = process.env.IN_FLY ? '0.0.0.0' : 'localhost';
|
|
441
|
+
if (process.env.IN_FLY) {
|
|
442
|
+
app.set('trust proxy', 2);
|
|
443
|
+
}
|
|
444
|
+
app.listen(port, host, () => console.log(`listening on port ${port}`));
|
|
445
|
+
}
|
|
446
|
+
async function main() {
|
|
447
|
+
if (process.env.SERVER_TYPE === 'http') {
|
|
448
|
+
return startSse();
|
|
449
|
+
}
|
|
450
|
+
return startStdio();
|
|
451
|
+
}
|
|
452
|
+
main().catch((error) => {
|
|
453
|
+
console.error('Fatal error in main():', error);
|
|
454
|
+
process.exit(1);
|
|
455
|
+
});
|
|
456
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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","sourcesContent":["#!/usr/bin/env node\n\nimport 'dotenv/config';\nimport express, { Request, Response } from 'express';\nimport { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';\nimport { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';\nimport { z } from 'zod';\nimport { parseArgs } from 'node:util';\nimport version from './version.ts';\nimport { StreamableHTTPServerTransport } from '@modelcontextprotocol/sdk/server/streamableHttp.js';\nimport { HoneycombSDK } from '@honeycombio/opentelemetry-node';\nimport {\n trace,\n SpanKind,\n SpanStatusCode,\n Tracer,\n Attributes,\n} from '@opentelemetry/api';\n\nimport {\n createOAuthMetadata,\n mcpAuthRouter,\n} from '@modelcontextprotocol/sdk/server/auth/router.js';\nimport { pinoHttp } from 'pino-http';\nimport { pino } from 'pino';\nimport { init } from '@interface-db/admin';\n\nimport schema from './db/instant.schema.ts';\nimport { requireBearerAuth } from '@modelcontextprotocol/sdk/server/auth/middleware/bearerAuth.js';\nimport {\n addOAuthRoutes,\n makeApiAuth,\n OAuthConfig,\n ServiceProvider,\n tokensOfBearerToken,\n} from './oauth-service-provider.ts';\nimport { KeyConfig } from './crypto.ts';\nimport { PlatformApi } from '@interface-db/platform';\nimport indexHtml from './index.html.ts';\nimport { SSEServerTransport } from '@modelcontextprotocol/sdk/server/sse.js';\nimport { handleQuery, handleTransact } from './tools.ts';\n\n// Helpers\n// -----------\nfunction createMCPServer(): McpServer {\n return new McpServer({\n name: '@interface-db/mcp',\n version,\n });\n}\n\n// Tool Registration\n// -----------\n\n// Adds tracing to server.tool\nfunction wrapServerWithTracing(\n server: McpServer,\n tracer: Tracer,\n attrs: Attributes,\n): McpServer {\n const originalTool = server.tool.bind(server);\n\n server.tool = function (name: string, ...args: any[]): any {\n // Find the callback (it's always the last argument)\n const callback = args[args.length - 1];\n const otherArgs = args.slice(0, -1);\n\n // Wrap the callback with tracing\n const wrappedCallback = async (...callbackArgs: any[]) => {\n const span = tracer.startSpan(`tool.${name}`, {\n attributes: attrs,\n });\n try {\n const result = await callback(...callbackArgs);\n span.setStatus({ code: SpanStatusCode.OK });\n return result;\n } catch (error) {\n span.setStatus({ code: SpanStatusCode.ERROR });\n span.recordException(error as Error);\n throw error;\n } finally {\n span.end();\n }\n };\n\n return originalTool(\n name,\n // @ts-expect-error: not sure how to type this\n ...otherArgs,\n wrappedCallback,\n );\n } as any;\n\n return server;\n}\n\nfunction registerTools(server: McpServer, api: PlatformApi) {\n server.tool(\n 'learn',\n \"If you don't have any context provided about InstantDB, use this tool to learn about it!\",\n {},\n async () => {\n const instructions = `\n You can learn about InstantDB by fetching our rules file for agents:\n\n https://www.interfacedb.com/llm-rules/AGENTS.md\n `;\n\n return {\n content: [\n {\n type: 'text',\n text: instructions,\n },\n ],\n };\n },\n );\n\n server.tool(\n 'get-schema',\n 'Fetch schema for an app by its ID!',\n {\n appId: z.string().uuid().describe('UUID of the app'),\n },\n async ({ appId }) => {\n const instructions = `\n You can fetch the schema for the app by using the instant-cli tool:\n\n \\`\\`\\`\n npx @interface-db/cli pull schema --app ${appId} --yes\n \\`\\`\\`\n\n We supply the --yes flag to skip confirmation prompts. Now 'instant.schema.ts' will contain the schema for the app.\n `;\n\n return {\n content: [\n {\n type: 'text',\n text: instructions,\n },\n ],\n };\n },\n );\n\n server.tool(\n 'get-perms',\n 'Fetch permissions for an app by its ID',\n {\n appId: z.string().uuid().describe('UUID of the app'),\n },\n async ({ appId }) => {\n const instructions = `\n You fetch the permissions for the app by using the instant-cli tool:\n\n \\`\\`\\`\n npx @interface-db/cli pull perms --app ${appId} --yes\n \\`\\`\\`\n\n We supply the --yes flag to skip confirmation prompts. Now 'instant.perms.ts' will contain the permissions for the app.\n `;\n\n return {\n content: [\n {\n type: 'text',\n text: instructions,\n },\n ],\n };\n },\n );\n\n server.tool(\n 'push-schema',\n `Push local schema changes for an app to the server. Do this after updating your local 'instant.schema.ts' file.\n If you don't have an instant.schema.ts file yet, use the get-schema tool to learn how to get this file.`,\n {\n appId: z.string().uuid().describe('UUID of the app'),\n },\n async ({ appId }) => {\n const instructions = `\n Push schema changes by using the instant-cli tool:\n\n \\`\\`\\`\n npx @interface-db/cli push schema --app ${appId} --yes\n \\`\\`\\`\n\n We supply the --yes flag to skip confirmation prompts.\n\n By default the instant-cli tool will assume new fields from the previous schema are additions and missing fields are deletions.\n If you want to rename fields as part of your schema changes you can use the --rename flag to specify renames.\n\n \\`\\`\\`\n npx @interface-db/cli push schema --app ${appId} --rename 'posts.author:posts.creator stores.owner:stores.manager' --yes\n \\`\\`\\`\n `;\n\n return {\n content: [\n {\n type: 'text',\n text: instructions,\n },\n ],\n };\n },\n );\n\n server.tool(\n 'push-perms',\n `Push local permissions changes for an app to the server. Do this after updating your local instant.perms.ts file.\n If you don't have an instant.perms.ts file yet, use the get-perms tool to learn how to get this file.`,\n {\n appId: z.string().uuid().describe('UUID of the app'),\n },\n async ({ appId }) => {\n const instructions = `\n Push permission changes by using the instant-cli tool:\n\n \\`\\`\\`\n npx @interface-db/cli push perms --app ${appId} --yes\n \\`\\`\\`\n\n We supply the --yes flag to skip confirmation prompts.\n `;\n\n return {\n content: [\n {\n type: 'text',\n text: instructions,\n },\n ],\n };\n },\n );\n\n server.tool(\n 'query',\n `Execute an InstaQL query against an app. Returns the query results as JSON.\n\n Example query to fetch all goals and their todos:\n {\"goals\": {\"todos\": {}}}\n\n Example query with a where clause:\n {\"goals\": {\"$\": {\"where\": {\"status\": \"active\"}}, \"todos\": {}}}\n\n If you're unsure how to write queries, refer to the documentation:\n https://interfacedb.com/docs/instaql`,\n {\n appId: z.string().uuid().describe('UUID of the app'),\n query: z.record(z.string(), z.any()).describe('InstaQL query object'),\n },\n async ({ appId, query }) => {\n return handleQuery(api, appId, query);\n },\n );\n\n server.tool(\n 'transact',\n `Execute a transaction against an app. Useful for creating, updating, or deleting data.\n\n Steps use the internal transaction format:\n - Create/update: [\"update\", \"namespace\", \"entity-id\", {\"attr\": \"value\"}]\n - Link: [\"link\", \"namespace\", \"entity-id\", {\"linkAttr\": \"target-id\"}]\n - Unlink: [\"unlink\", \"namespace\", \"entity-id\", {\"linkAttr\": \"target-id\"}]\n - Delete: [\"delete\", \"namespace\", \"entity-id\"]\n\n Example steps to create a todo:\n [[\"update\", \"todos\", \"a-uuid\", {\"title\": \"Get fit\", \"done\": false}]]\n\n If you're unsure how to make transactions, refer to the documentation:\n https://interfacedb.com/docs/instaml`,\n {\n appId: z.string().uuid().describe('UUID of the app'),\n steps: z.array(z.array(z.any())).describe('Array of transaction steps'),\n },\n async ({ appId, steps }) => {\n return handleTransact(api, appId, steps);\n },\n );\n}\n\nasync function startStdio() {\n const {\n values: { token, ['api-url']: apiUrl },\n } = parseArgs({\n options: {\n token: {\n type: 'string',\n },\n ['api-url']: {\n type: 'string',\n },\n },\n });\n\n const accessToken = token || process.env.INSTANT_ACCESS_TOKEN;\n if (!accessToken) {\n console.error(\n 'Provide an access token using --token or set INSTANT_ACCESS_TOKEN environment variable',\n );\n process.exit(1);\n }\n\n const api = new PlatformApi({ auth: { token: accessToken } });\n\n const server = createMCPServer();\n registerTools(server, api);\n\n const transport = new StdioServerTransport();\n await server.connect(transport);\n console.error('Instant Platform MCP Server running on stdio');\n}\n\nfunction ensureEnv(key: string): string {\n const v = process.env[key];\n if (!v) {\n throw new Error(`Missing environment variable ${key}`);\n }\n return v;\n}\n\nasync function startSse() {\n const honeycomb = new HoneycombSDK({\n apiKey: process.env.HONEYCOMB_API_KEY,\n serviceName: 'mcp-server',\n });\n\n if (process.env.HONEYCOMB_API_KEY) {\n honeycomb.start();\n }\n\n const tracer = trace.getTracer('mcp-server');\n\n const db = init({\n adminToken: ensureEnv('INSTANT_ADMIN_TOKEN'),\n appId: ensureEnv('INSTANT_APP_ID'),\n schema,\n disableValidation: true,\n });\n\n const oauthConfig: OAuthConfig = {\n clientId: ensureEnv('INSTANT_OAUTH_CLIENT_ID'),\n clientSecret: ensureEnv('INSTANT_OAUTH_CLIENT_SECRET'),\n serverOrigin: ensureEnv('SERVER_ORIGIN'),\n };\n\n const keyConfig: KeyConfig = JSON.parse(ensureEnv('INSTANT_AES_KEY'));\n\n const app = express();\n const logger = pino({ level: 'info' });\n\n app.use((req, res, next) => {\n const span = tracer.startSpan('http-req', {\n kind: SpanKind.SERVER,\n attributes: {\n 'http.method': req.method,\n 'http.url': req.url,\n 'http.target': req.path,\n 'http.host': req.get('host'),\n 'http.scheme': req.protocol,\n },\n });\n\n const originalEnd = res.end.bind(res);\n res.end = function (this: typeof res, ...args: any[]): typeof res {\n span.setAttribute('http.status_code', res.statusCode);\n span.setStatus({\n code: res.statusCode >= 400 ? SpanStatusCode.ERROR : SpanStatusCode.OK,\n });\n span.end();\n return originalEnd(...args);\n };\n\n next();\n });\n\n app.use(\n pinoHttp({\n logger,\n autoLogging: {\n ignore(req) {\n return req.url === '/health';\n },\n },\n }),\n );\n app.use(express.json());\n\n const proxyProvider = new ServiceProvider(db, oauthConfig, keyConfig);\n\n const authRouterOptions = {\n scopesSupported: ['apps-read', 'apps-write'],\n provider: proxyProvider,\n issuerUrl: new URL(oauthConfig.serverOrigin),\n baseUrl: new URL(oauthConfig.serverOrigin),\n serviceDocumentationUrl: new URL('https://interfacedb.com/docs'),\n };\n\n const oauthMetadata = createOAuthMetadata(authRouterOptions);\n\n app.use(mcpAuthRouter(authRouterOptions));\n\n addOAuthRoutes(app, db, oauthConfig);\n\n app.get('/.well-known/oauth-protected-resource/mcp', (_req, res) => {\n res.json({\n resource: `${oauthConfig.serverOrigin}/mcp`,\n authorization_servers: [oauthMetadata.issuer],\n scopes_supported: oauthMetadata.scopes_supported,\n resource_documentation: 'https://interfacedb.com/docs',\n });\n });\n\n app.get('/.well-known/oauth-protected-resource/sse', (_req, res) => {\n res.json({\n resource: `${oauthConfig.serverOrigin}/mcp`,\n authorization_servers: [oauthMetadata.issuer],\n scopes_supported: oauthMetadata.scopes_supported,\n resource_documentation: 'https://interfacedb.com/docs',\n });\n });\n\n const requireTokenMiddleware = (path: string) =>\n requireBearerAuth({\n verifier: proxyProvider,\n resourceMetadataUrl: `${oauthConfig.serverOrigin}/.well-known/oauth-protected-resource/${path}`,\n });\n\n // Handle POST requests for client-to-server communication\n app.post(\n '/mcp',\n requireTokenMiddleware('mcp'),\n async (req: Request, res: Response) => {\n const server = createMCPServer();\n try {\n const tokens = await tokensOfBearerToken(db, req.auth!.token);\n\n const api = new PlatformApi({\n auth: makeApiAuth(oauthConfig, keyConfig, db, tokens.instantToken),\n });\n\n wrapServerWithTracing(server, tracer, {\n 'client.client_id': tokens.mcpToken.client?.client_id,\n 'client.name': tokens.mcpToken.client?.client_name,\n 'client.id': tokens.mcpToken.client?.id,\n 'client.scope': tokens.mcpToken.client?.scope,\n 'client.uri': tokens.mcpToken.client?.client_uri,\n 'client.redirect_urls': tokens.mcpToken.client?.redirect_uris,\n });\n registerTools(server, api);\n const transport: StreamableHTTPServerTransport =\n new StreamableHTTPServerTransport({\n sessionIdGenerator: undefined,\n });\n\n req.on('close', () => {\n transport.close();\n server.close();\n });\n await server.connect(transport);\n await transport.handleRequest(req, res, req.body);\n } catch (e) {\n console.error('Error handling MCP request:', e);\n if (!res.headersSent) {\n res.status(500).json({\n jsonrpc: '2.0',\n error: {\n code: -32603,\n message: 'Internal server error',\n },\n id: null,\n });\n }\n }\n },\n );\n\n // We're a stateless server, so disallow these\n const handleSessionRequest = async (\n _req: express.Request,\n res: express.Response,\n ) => {\n res.writeHead(405).end(\n JSON.stringify({\n jsonrpc: '2.0',\n error: {\n code: -32000,\n message: 'Method not allowed.',\n },\n id: null,\n }),\n );\n };\n\n app.get('/mcp', handleSessionRequest);\n app.delete('/mcp', handleSessionRequest);\n\n // SSE for older clients\n const transports = {\n sse: {} as Record<string, SSEServerTransport>,\n };\n\n app.get(\n '/sse',\n requireTokenMiddleware('sse'),\n async (req: Request, res: Response) => {\n const server = createMCPServer();\n const transport = new SSEServerTransport('/messages', res);\n res.on('close', () => {\n delete transports.sse[transport.sessionId];\n });\n\n try {\n const tokens = await tokensOfBearerToken(db, req.auth!.token);\n\n const api = new PlatformApi({\n auth: makeApiAuth(oauthConfig, keyConfig, db, tokens.instantToken),\n });\n\n wrapServerWithTracing(server, tracer, {\n 'client.client_id': tokens.mcpToken.client?.client_id,\n 'client.name': tokens.mcpToken.client?.client_name,\n 'client.id': tokens.mcpToken.client?.id,\n 'client.scope': tokens.mcpToken.client?.scope,\n 'client.uri': tokens.mcpToken.client?.client_uri,\n 'client.redirect_urls': tokens.mcpToken.client?.redirect_uris,\n });\n\n registerTools(server, api);\n transports.sse[transport.sessionId] = transport;\n } catch (e) {\n console.error('Error handling MCP SSE request:', e);\n if (!res.headersSent) {\n res.status(500).json({\n jsonrpc: '2.0',\n error: {\n code: -32603,\n message: 'Internal server error',\n },\n id: null,\n });\n }\n return;\n }\n\n await server.connect(transport);\n },\n );\n\n // Legacy message endpoint for older clients\n app.post('/messages', async (req, res) => {\n const sessionId = req.query.sessionId as string;\n const transport = transports.sse[sessionId];\n if (transport) {\n await transport.handlePostMessage(req, res, req.body);\n } else {\n res.status(400).send('No transport found for sessionId');\n }\n });\n\n app.get('/', (_req, res: Response) => {\n res\n .status(200)\n .set('Content-Type', 'text/html; charset=UTF-8')\n .send(indexHtml(oauthConfig.serverOrigin));\n });\n\n app.get('/health', (_req, res: Response) => {\n res.status(200).send('Tip top!');\n });\n\n const port = parseInt(process.env.PORT || '3123');\n const host = process.env.IN_FLY ? '0.0.0.0' : 'localhost';\n\n if (process.env.IN_FLY) {\n app.set('trust proxy', 2);\n }\n\n app.listen(port, host, () => console.log(`listening on port ${port}`));\n}\n\nasync function main() {\n if (process.env.SERVER_TYPE === 'http') {\n return startSse();\n }\n return startStdio();\n}\n\nmain().catch((error) => {\n console.error('Fatal error in main():', error);\n process.exit(1);\n});\n"]}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { Express, Response } from 'express';
|
|
2
|
+
import { InstantAdminDatabase, InstaQLEntity, InstaQLResult } from '@interface-db/admin';
|
|
3
|
+
import { AppSchema } from './db/instant.schema.ts';
|
|
4
|
+
import { AuthorizationParams, OAuthServerProvider } from '@modelcontextprotocol/sdk/server/auth/provider.js';
|
|
5
|
+
import { OAuthRegisteredClientsStore } from '@modelcontextprotocol/sdk/server/auth/clients.js';
|
|
6
|
+
import { OAuthClientInformationFull, OAuthTokenRevocationRequest, OAuthTokens } from '@modelcontextprotocol/sdk/shared/auth.js';
|
|
7
|
+
import { KeyConfig } from './crypto.ts';
|
|
8
|
+
import { AuthInfo } from '@modelcontextprotocol/sdk/server/auth/types.js';
|
|
9
|
+
import { PlatformApiAuth } from '../../platform/dist/esm/api.js';
|
|
10
|
+
export type OAuthConfig = {
|
|
11
|
+
clientId: string;
|
|
12
|
+
clientSecret: string;
|
|
13
|
+
serverOrigin: string;
|
|
14
|
+
};
|
|
15
|
+
export declare function tokensOfBearerToken(db: InstantAdminDatabase<AppSchema>, token: string): Promise<{
|
|
16
|
+
mcpToken: InstaQLResult<AppSchema, {
|
|
17
|
+
mcpTokens: {
|
|
18
|
+
client: {};
|
|
19
|
+
instantToken: {};
|
|
20
|
+
};
|
|
21
|
+
}>['mcpTokens'][number];
|
|
22
|
+
instantToken: InstaQLEntity<AppSchema, 'instantTokens'>;
|
|
23
|
+
}>;
|
|
24
|
+
export declare function makeApiAuth(oauthConfig: OAuthConfig, key: KeyConfig, db: InstantAdminDatabase<AppSchema>, instantTokenEnt: InstaQLEntity<AppSchema, 'instantTokens'>): PlatformApiAuth;
|
|
25
|
+
export declare class ServiceProvider implements OAuthServerProvider {
|
|
26
|
+
#private;
|
|
27
|
+
constructor(db: InstantAdminDatabase<AppSchema>, oauthConfig: OAuthConfig, keyConfig: KeyConfig);
|
|
28
|
+
get clientsStore(): OAuthRegisteredClientsStore;
|
|
29
|
+
authorize(client: OAuthClientInformationFull, params: AuthorizationParams, res: Response): Promise<void>;
|
|
30
|
+
challengeForAuthorizationCode(_client: OAuthClientInformationFull, authorizationCode: string): Promise<string>;
|
|
31
|
+
exchangeAuthorizationCode(client: OAuthClientInformationFull, authorizationCode: string, _codeVerifier?: string, redirectUri?: string): Promise<OAuthTokens>;
|
|
32
|
+
exchangeRefreshToken(client: OAuthClientInformationFull, refreshToken: string, _scopes?: string[]): Promise<OAuthTokens>;
|
|
33
|
+
verifyAccessToken(token: string): Promise<AuthInfo>;
|
|
34
|
+
revokeToken(_client: OAuthClientInformationFull, request: OAuthTokenRevocationRequest): Promise<void>;
|
|
35
|
+
}
|
|
36
|
+
export declare function addOAuthRoutes(app: Express, db: InstantAdminDatabase<AppSchema>, oauthConfig: OAuthConfig): void;
|
|
37
|
+
//# sourceMappingURL=oauth-service-provider.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"oauth-service-provider.d.ts","sourceRoot":"","sources":["../src/oauth-service-provider.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,OAAO,EAIP,QAAQ,EAET,MAAM,SAAS,CAAC;AAEjB,OAAO,EAEL,oBAAoB,EACpB,aAAa,EACb,aAAa,EAEd,MAAM,qBAAqB,CAAC;AAC7B,OAAO,EAAE,SAAS,EAAE,MAAM,wBAAwB,CAAC;AACnD,OAAO,EACL,mBAAmB,EACnB,mBAAmB,EACpB,MAAM,mDAAmD,CAAC;AAC3D,OAAO,EAAE,2BAA2B,EAAE,MAAM,kDAAkD,CAAC;AAC/F,OAAO,EACL,0BAA0B,EAC1B,2BAA2B,EAC3B,WAAW,EACZ,MAAM,0CAA0C,CAAC;AAClD,OAAO,EAA0B,SAAS,EAAE,MAAM,aAAa,CAAC;AAEhE,OAAO,EAAE,QAAQ,EAAE,MAAM,gDAAgD,CAAC;AAC1E,OAAO,EAAE,eAAe,EAAE,MAAM,gCAAgC,CAAC;AAQjE,MAAM,MAAM,WAAW,GAAG;IACxB,QAAQ,EAAE,MAAM,CAAC;IACjB,YAAY,EAAE,MAAM,CAAC;IACrB,YAAY,EAAE,MAAM,CAAC;CACtB,CAAC;AAEF,wBAAsB,mBAAmB,CACvC,EAAE,EAAE,oBAAoB,CAAC,SAAS,CAAC,EACnC,KAAK,EAAE,MAAM,GACZ,OAAO,CAAC;IACT,QAAQ,EAAE,aAAa,CACrB,SAAS,EACT;QAAE,SAAS,EAAE;YAAE,MAAM,EAAE,EAAE,CAAC;YAAC,YAAY,EAAE,EAAE,CAAA;SAAE,CAAA;KAAE,CAChD,CAAC,WAAW,CAAC,CAAC,MAAM,CAAC,CAAC;IACvB,YAAY,EAAE,aAAa,CAAC,SAAS,EAAE,eAAe,CAAC,CAAC;CACzD,CAAC,CAmBD;AAED,wBAAgB,WAAW,CACzB,WAAW,EAAE,WAAW,EACxB,GAAG,EAAE,SAAS,EACd,EAAE,EAAE,oBAAoB,CAAC,SAAS,CAAC,EACnC,eAAe,EAAE,aAAa,CAAC,SAAS,EAAE,eAAe,CAAC,GACzD,eAAe,CA2BjB;AAmBD,qBAAa,eAAgB,YAAW,mBAAmB;;gBAMvD,EAAE,EAAE,oBAAoB,CAAC,SAAS,CAAC,EACnC,WAAW,EAAE,WAAW,EACxB,SAAS,EAAE,SAAS;IAOtB,IAAI,YAAY,IAAI,2BAA2B,CA+C9C;IACK,SAAS,CACb,MAAM,EAAE,0BAA0B,EAClC,MAAM,EAAE,mBAAmB,EAC3B,GAAG,EAAE,QAAQ,GACZ,OAAO,CAAC,IAAI,CAAC;IA4BV,6BAA6B,CACjC,OAAO,EAAE,0BAA0B,EACnC,iBAAiB,EAAE,MAAM,GACxB,OAAO,CAAC,MAAM,CAAC;IAsBZ,yBAAyB,CAC7B,MAAM,EAAE,0BAA0B,EAClC,iBAAiB,EAAE,MAAM,EAEzB,aAAa,CAAC,EAAE,MAAM,EACtB,WAAW,CAAC,EAAE,MAAM,GACnB,OAAO,CAAC,WAAW,CAAC;IAyFjB,oBAAoB,CACxB,MAAM,EAAE,0BAA0B,EAClC,YAAY,EAAE,MAAM,EACpB,OAAO,CAAC,EAAE,MAAM,EAAE,GACjB,OAAO,CAAC,WAAW,CAAC;IA8CjB,iBAAiB,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,QAAQ,CAAC;IA8BnD,WAAW,CACf,OAAO,EAAE,0BAA0B,EACnC,OAAO,EAAE,2BAA2B,GACnC,OAAO,CAAC,IAAI,CAAC;CAQjB;AAiYD,wBAAgB,cAAc,CAC5B,GAAG,EAAE,OAAO,EACZ,EAAE,EAAE,oBAAoB,CAAC,SAAS,CAAC,EACnC,WAAW,EAAE,WAAW,QA4CzB"}
|