| 1 | import descopeSdk from '@descope/node-sdk';
|
| 2 | import type { AuthInfo } from '@modelcontextprotocol/server';
|
| 3 | import { createMcpHandler, withMcpAuth } from 'mcp-handler';
|
| 4 | import { TrustEngine } from '../packages/core/src/index.js';
|
| 5 | import { registerOpenClaspTools } from '../packages/mcp-server/src/server.js';
|
| 6 |
|
| 7 | const engine = new TrustEngine();
|
| 8 | const mcp = createMcpHandler((server) => registerOpenClaspTools(server, engine), {
|
| 9 | serverInfo: { name: 'openclasp', version: '0.1.0' },
|
| 10 | instructions:
|
| 11 | 'OpenClasp provides identity, interaction assurance, private clues, receipts, and contextual behavioural history. Never submit raw private conversations.',
|
| 12 | });
|
| 13 |
|
| 14 | async function verifyToken(_request: Request, bearerToken?: string): Promise<AuthInfo | undefined> {
|
| 15 | const projectId = process.env.DESCOPE_PROJECT_ID ?? process.env.NEXT_PUBLIC_DESCOPE_PROJECT_ID;
|
| 16 | if (!projectId || !bearerToken) return undefined;
|
| 17 | const descope = descopeSdk({ projectId });
|
| 18 | const authentication = await descope.validateSession(bearerToken, {
|
| 19 | ...(process.env.OPENCLASP_MCP_URL ? { audience: process.env.OPENCLASP_MCP_URL } : {}),
|
| 20 | });
|
| 21 | if (!authentication.token.exp) return undefined;
|
| 22 | return {
|
| 23 | token: bearerToken,
|
| 24 | clientId: authentication.token.sub ?? 'unknown-client',
|
| 25 | scopes: descope.getJwtPermissions(bearerToken),
|
| 26 | expiresAt: authentication.token.exp,
|
| 27 | };
|
| 28 | }
|
| 29 |
|
| 30 | const handler = withMcpAuth(mcp, verifyToken, {
|
| 31 | required: true,
|
| 32 | requiredScopes: ['profile:read'],
|
| 33 | resourceMetadataPath: '/.well-known/oauth-protected-resource',
|
| 34 | ...(process.env.OPENCLASP_MCP_URL
|
| 35 | ? { resourceUrl: new URL(process.env.OPENCLASP_MCP_URL).origin }
|
| 36 | : {}),
|
| 37 | });
|
| 38 |
|
| 39 | export { handler as GET, handler as POST };
|