@sendinel/mcp-server 1.0.90 → 1.0.91
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/build/audit.js +1 -0
- package/build/auth.d.ts +1 -0
- package/build/auth.js +12 -1
- package/build/db.d.ts +2 -0
- package/build/db.js +1 -0
- package/build/index.js +1 -0
- package/build/scoped-token.d.ts +1 -0
- package/build/scoped-token.js +3 -1
- package/package.json +1 -1
package/build/audit.js
CHANGED
|
@@ -36,6 +36,7 @@ export async function logAudit(opts) {
|
|
|
36
36
|
resource_id: opts.resourceId ?? null,
|
|
37
37
|
details: {
|
|
38
38
|
...(opts.details ?? {}),
|
|
39
|
+
...(context?.actorType === 'synchronex_scoped_jwt' ? { kid: context.kid ?? null } : {}),
|
|
39
40
|
...(context?.workspaceId ? { workspace_id: context.workspaceId } : {}),
|
|
40
41
|
// #4004: same actor + agent identity the dashboard records in api_usage. Only fields known for
|
|
41
42
|
// this request are written; nothing is defaulted.
|
package/build/auth.d.ts
CHANGED
package/build/auth.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { decodeProtectedHeader, errors } from 'jose';
|
|
1
2
|
import { adminDb } from './db.js';
|
|
2
3
|
import { ALL_TOOL_GROUPS } from './server.js';
|
|
3
4
|
import { looksLikeScopedJwt, scopedJwtVerificationEnabled, verifyScopedToken, } from './scoped-token.js';
|
|
@@ -82,7 +83,15 @@ export async function authenticateBearer(token, overrideProjectId) {
|
|
|
82
83
|
try {
|
|
83
84
|
claims = await verifyScopedToken(token);
|
|
84
85
|
}
|
|
85
|
-
catch {
|
|
86
|
+
catch (error) {
|
|
87
|
+
let kid = null;
|
|
88
|
+
try {
|
|
89
|
+
const header = decodeProtectedHeader(token);
|
|
90
|
+
kid = typeof header.kid === 'string' ? header.kid.slice(0, 64) : null;
|
|
91
|
+
}
|
|
92
|
+
catch { /* Unreadable headers have an unknown key id. */ }
|
|
93
|
+
const code = error instanceof errors.JOSEError ? error.code : null;
|
|
94
|
+
console.warn('[scoped-jwt] verification failed', { kid, code });
|
|
86
95
|
throw new McpAuthenticationError('Invalid or expired token', 401, 'INVALID_SCOPED_TOKEN');
|
|
87
96
|
}
|
|
88
97
|
if (claims.scope === 'data'
|
|
@@ -107,6 +116,7 @@ export async function authenticateBearer(token, overrideProjectId) {
|
|
|
107
116
|
action: 'mcp.authenticate',
|
|
108
117
|
resource_type: 'mcp_transport',
|
|
109
118
|
details: {
|
|
119
|
+
kid: claims.kid ?? null,
|
|
110
120
|
workspace_id: claims.workspaceId,
|
|
111
121
|
token_scope: claims.scope,
|
|
112
122
|
},
|
|
@@ -120,6 +130,7 @@ export async function authenticateBearer(token, overrideProjectId) {
|
|
|
120
130
|
authMethod: 'scoped_jwt',
|
|
121
131
|
actsAsUserId: claims.userId,
|
|
122
132
|
workspaceId: claims.workspaceId,
|
|
133
|
+
kid: claims.kid,
|
|
123
134
|
};
|
|
124
135
|
}
|
|
125
136
|
let info = await validateKey(token);
|
package/build/db.d.ts
CHANGED
|
@@ -44,6 +44,7 @@ export declare function runWithClient<T>(client: any, fn: () => Promise<T>, requ
|
|
|
44
44
|
agentLabel?: string | null;
|
|
45
45
|
keyId?: string | null;
|
|
46
46
|
workspaceId?: string | null;
|
|
47
|
+
kid?: string | null;
|
|
47
48
|
v1Caller?: V1Caller;
|
|
48
49
|
}): Promise<T>;
|
|
49
50
|
export declare function runWithToolContext<T>(toolGroup: string, toolName: string, fn: () => Promise<T> | T): Promise<T>;
|
|
@@ -65,6 +66,7 @@ export declare function requestContext(): {
|
|
|
65
66
|
agentLabel?: string | null;
|
|
66
67
|
keyId?: string | null;
|
|
67
68
|
workspaceId?: string | null;
|
|
69
|
+
kid?: string | null;
|
|
68
70
|
v1Caller?: V1Caller;
|
|
69
71
|
} | undefined;
|
|
70
72
|
export declare function projectClientForWriteback(): any | null;
|
package/build/db.js
CHANGED
package/build/index.js
CHANGED
|
@@ -115,6 +115,7 @@ async function runHttp() {
|
|
|
115
115
|
actorId: info.actsAsUserId ?? null,
|
|
116
116
|
authKind: info.authMethod === 'scoped_jwt' ? 'scoped_jwt' : 'api_key',
|
|
117
117
|
workspaceId: info.workspaceId ?? null,
|
|
118
|
+
kid: info.kid ?? null,
|
|
118
119
|
});
|
|
119
120
|
});
|
|
120
121
|
app.get('/health', (_req, res) => res.json({ ok: true }));
|
package/build/scoped-token.d.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
export declare const SCOPED_TOKEN_SCOPES: readonly ['data', 'service:provision'];
|
|
2
2
|
export type ScopedTokenScope = (typeof SCOPED_TOKEN_SCOPES)[number];
|
|
3
3
|
export type ScopedTokenClaims = {
|
|
4
|
+
kid: string | null;
|
|
4
5
|
userId: string;
|
|
5
6
|
projectId: string | null;
|
|
6
7
|
workspaceId: string | null;
|
package/build/scoped-token.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { createRemoteJWKSet, jwtVerify } from 'jose';
|
|
1
|
+
import { createRemoteJWKSet, decodeProtectedHeader, jwtVerify } from 'jose';
|
|
2
2
|
const ISSUER = 'synchronex';
|
|
3
3
|
const AUDIENCE = 'sendinel';
|
|
4
4
|
const ALGORITHM = 'RS256';
|
|
@@ -50,7 +50,9 @@ export async function verifyScopedToken(token) {
|
|
|
50
50
|
if (payload.workspace_id !== null && payload.workspace_id !== undefined && typeof payload.workspace_id !== 'string') {
|
|
51
51
|
throw new Error('Scoped token workspace claim is invalid');
|
|
52
52
|
}
|
|
53
|
+
const { kid } = decodeProtectedHeader(token);
|
|
53
54
|
return {
|
|
55
|
+
kid: typeof kid === 'string' ? kid : null,
|
|
54
56
|
userId,
|
|
55
57
|
projectId,
|
|
56
58
|
workspaceId,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@sendinel/mcp-server",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.91",
|
|
4
4
|
"description": "AI-controlled email operations control plane for campaigns, contacts, segments, analytics, and multi-provider sending via MCP.",
|
|
5
5
|
"mcpName": "ai.sendinel/mcp-server",
|
|
6
6
|
"type": "module",
|