@musashishao/agent-kit 1.9.0 → 1.9.1

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.
Files changed (125) hide show
  1. package/.agent/agents/ai-asset-factory.md +700 -0
  2. package/.agent/agents/ai-audio-factory.md +503 -0
  3. package/.agent/agents/game-developer.md +4 -4
  4. package/.agent/agents/orchestrator.md +113 -3
  5. package/.agent/agents/project-planner.md +67 -0
  6. package/.agent/agents/unity-mobile-master.md +949 -0
  7. package/.agent/mcp/config/registry.json +65 -51
  8. package/.agent/mcp/servers/notebooklm/README.md +114 -0
  9. package/.agent/mcp/servers/notebooklm/package.json +35 -0
  10. package/.agent/mcp/servers/notebooklm/src/auth/chrome.ts +225 -0
  11. package/.agent/mcp/servers/notebooklm/src/auth/index.ts +1 -0
  12. package/.agent/mcp/servers/notebooklm/src/index.ts +516 -0
  13. package/.agent/mcp/servers/notebooklm/src/services/index.ts +3 -0
  14. package/.agent/mcp/servers/notebooklm/src/services/library.ts +217 -0
  15. package/.agent/mcp/servers/notebooklm/src/services/notebooklm.ts +380 -0
  16. package/.agent/mcp/servers/notebooklm/tsconfig.json +15 -0
  17. package/.agent/mcp-gateway/README.md +169 -20
  18. package/.agent/mcp-gateway/package.json +22 -7
  19. package/.agent/mcp-gateway/src/auth/index.ts +55 -0
  20. package/.agent/mcp-gateway/src/auth/middleware.ts +242 -0
  21. package/.agent/mcp-gateway/src/auth/oauth.ts +462 -0
  22. package/.agent/mcp-gateway/src/auth/scopes.ts +227 -0
  23. package/.agent/mcp-gateway/src/index.ts +252 -105
  24. package/.agent/mcp-gateway/src/observability/index.ts +5 -0
  25. package/.agent/mcp-gateway/src/observability/otel.ts +405 -0
  26. package/.agent/mcp-gateway/src/transports/index.ts +5 -0
  27. package/.agent/mcp-gateway/src/transports/streamableHttp.ts +235 -0
  28. package/.agent/rules/CODEX.md +89 -0
  29. package/.agent/rules/CODE_RULES.md +73 -0
  30. package/.agent/rules/GEMINI.md +25 -0
  31. package/.agent/rules/MEMORY_STATE.md +110 -0
  32. package/.agent/rules/REFERENCE.md +33 -141
  33. package/.agent/rules/REF_SKILLS.md +116 -0
  34. package/.agent/rules/REF_WORKFLOWS.md +81 -0
  35. package/.agent/scripts/ak_cli.py +106 -5
  36. package/.agent/scripts/memory_manager.py +48 -9
  37. package/.agent/skills/anti-hallucination/SKILL.md +295 -0
  38. package/.agent/skills/anti-hallucination/scripts/check_hallucination.py +299 -0
  39. package/.agent/skills/bifurcation-analysis/SKILL.md +56 -0
  40. package/.agent/skills/brainstorming/SKILL.md +80 -6
  41. package/.agent/skills/decision-memory/SKILL.md +317 -0
  42. package/.agent/skills/emergence-detector/SKILL.md +230 -0
  43. package/.agent/skills/emergence-detector/scripts/check_emergence.py +265 -0
  44. package/.agent/skills/explained-qa/SKILL.md +142 -0
  45. package/.agent/skills/explained-qa/game-terminology.md +214 -0
  46. package/.agent/skills/game-development/ai-dialogue-engine/SKILL.md +442 -0
  47. package/.agent/skills/game-development/ai-graphics-generator/SKILL.md +463 -0
  48. package/.agent/skills/game-development/ai-playtest-framework/SKILL.md +570 -0
  49. package/.agent/skills/game-development/camera-systems/SKILL.md +607 -0
  50. package/.agent/skills/game-development/card-battle-engine/SKILL.md +618 -0
  51. package/.agent/skills/game-development/character-controller-3d/SKILL.md +908 -0
  52. package/.agent/skills/game-development/cloud-save-sync/SKILL.md +527 -0
  53. package/.agent/skills/game-development/combat-system/SKILL.md +748 -0
  54. package/.agent/skills/game-development/compliance-rating/SKILL.md +277 -0
  55. package/.agent/skills/game-development/crossplatform-build/SKILL.md +386 -0
  56. package/.agent/skills/game-development/cultivation-progression/SKILL.md +520 -0
  57. package/.agent/skills/game-development/data-driven-balance/SKILL.md +535 -0
  58. package/.agent/skills/game-development/game-analytics-integrator/SKILL.md +410 -0
  59. package/.agent/skills/game-development/game-audio-advanced/SKILL.md +646 -0
  60. package/.agent/skills/game-development/game-economy-designer/SKILL.md +375 -0
  61. package/.agent/skills/game-development/game-marketing/SKILL.md +85 -0
  62. package/.agent/skills/game-development/game-state-manager/SKILL.md +883 -0
  63. package/.agent/skills/game-development/hybrid-game-spec/SKILL.md +220 -0
  64. package/.agent/skills/game-development/inventory-quest/SKILL.md +747 -0
  65. package/.agent/skills/game-development/liveops/SKILL.md +308 -0
  66. package/.agent/skills/game-development/localization/SKILL.md +286 -0
  67. package/.agent/skills/game-development/mobile-input-patterns/SKILL.md +343 -0
  68. package/.agent/skills/game-development/monetization-strategy/SKILL.md +94 -0
  69. package/.agent/skills/game-development/multiplayer-master/SKILL.md +727 -0
  70. package/.agent/skills/game-development/narrative-branching/SKILL.md +593 -0
  71. package/.agent/skills/game-development/procedural-level-ai/SKILL.md +367 -0
  72. package/.agent/skills/game-development/prototyping-rapid/SKILL.md +205 -0
  73. package/.agent/skills/game-development/spec-ecosystem/SKILL.md +155 -0
  74. package/.agent/skills/game-development/spec-ecosystem/decision-log-format.md +129 -0
  75. package/.agent/skills/game-development/spec-ecosystem/templates/PLAN-template.md +178 -0
  76. package/.agent/skills/game-development/spec-ecosystem/templates/SPEC-template.md +110 -0
  77. package/.agent/skills/game-development/spec-ecosystem/templates/TASKS-template.md +156 -0
  78. package/.agent/skills/game-development/survival-systems/SKILL.md +493 -0
  79. package/.agent/skills/game-development/testing-qa/SKILL.md +270 -0
  80. package/.agent/skills/game-development/unity-mobile-optimization/SKILL.md +271 -0
  81. package/.agent/skills/intent-capture/SKILL.md +65 -0
  82. package/.agent/skills/mcp-composition/SKILL.md +362 -0
  83. package/.agent/skills/mcp-observability/SKILL.md +323 -0
  84. package/.agent/skills/mcp-security/SKILL.md +314 -0
  85. package/.agent/skills/trust-spectrum/SKILL.md +291 -0
  86. package/.agent/skills/vibe-coding-guard/SKILL.md +328 -0
  87. package/.agent/templates/AGENTS.game.md +63 -0
  88. package/.agent/templates/docs/WORKFLOW_GUIDE.en.md +100 -0
  89. package/.agent/templates/docs/WORKFLOW_GUIDE.vi.md +100 -0
  90. package/.agent/workflows/ai-agent.md +2 -0
  91. package/.agent/workflows/autofix.md +1 -0
  92. package/.agent/workflows/brainstorm.md +1 -0
  93. package/.agent/workflows/context.md +1 -0
  94. package/.agent/workflows/create.md +39 -8
  95. package/.agent/workflows/dashboard.md +1 -0
  96. package/.agent/workflows/debug.md +14 -0
  97. package/.agent/workflows/deploy.md +14 -0
  98. package/.agent/workflows/enhance.md +44 -0
  99. package/.agent/workflows/gamekit-init.md +177 -0
  100. package/.agent/workflows/gamekit-launch.md +338 -0
  101. package/.agent/workflows/gamekit-plan.md +204 -0
  102. package/.agent/workflows/gamekit-qa.md +153 -0
  103. package/.agent/workflows/gamekit-spec.md +243 -0
  104. package/.agent/workflows/gamekit-tasks.md +208 -0
  105. package/.agent/workflows/marketing.md +2 -0
  106. package/.agent/workflows/next.md +1 -0
  107. package/.agent/workflows/orchestrate.md +12 -0
  108. package/.agent/workflows/pentest.md +2 -0
  109. package/.agent/workflows/plan.md +42 -0
  110. package/.agent/workflows/preview.md +1 -0
  111. package/.agent/workflows/quality.md +1 -0
  112. package/.agent/workflows/saas.md +2 -0
  113. package/.agent/workflows/spec.md +42 -0
  114. package/.agent/workflows/status.md +1 -0
  115. package/.agent/workflows/test.md +14 -0
  116. package/.agent/workflows/ui-ux-pro-max.md +1 -0
  117. package/bin/cli.js +411 -111
  118. package/package.json +1 -2
  119. package/.agent/agents/game-asset-curator.md +0 -317
  120. package/.agent/agents/game-narrative-designer.md +0 -310
  121. package/.agent/agents/game-qa-agent.md +0 -441
  122. package/.agent/workflows/game-prototype.md +0 -154
  123. package/docs/AI_DATA_INFRASTRUCTURE.md +0 -288
  124. package/docs/CHANGELOG_AI_INFRA.md +0 -141
  125. package/docs/MIGRATION_GUIDE_V1.9.md +0 -55
@@ -0,0 +1,462 @@
1
+ /**
2
+ * OAuth 2.1 Authentication for MCP Gateway
3
+ *
4
+ * Implements:
5
+ * - OAuth 2.1 IETF DRAFT compliance
6
+ * - PKCE (Proof Key for Code Exchange) - mandatory
7
+ * - Dynamic Client Registration (RFC 7591)
8
+ * - Resource Indicators (RFC 8707)
9
+ * - Scope-based access control
10
+ *
11
+ * @see https://modelcontextprotocol.io/docs/authorization
12
+ */
13
+
14
+ import type { IncomingMessage, ServerResponse } from "http";
15
+ import * as crypto from "crypto";
16
+
17
+ // ============================================================================
18
+ // Types
19
+ // ============================================================================
20
+
21
+ export interface OAuthConfig {
22
+ /** Issuer URL for token validation */
23
+ issuer: string;
24
+ /** Client ID for this MCP server */
25
+ clientId: string;
26
+ /** Client secret (for confidential clients) */
27
+ clientSecret?: string;
28
+ /** Redirect URIs for authorization flow */
29
+ redirectUris: string[];
30
+ /** Supported scopes */
31
+ scopes: string[];
32
+ /** Token endpoint */
33
+ tokenEndpoint?: string;
34
+ /** Authorization endpoint */
35
+ authorizationEndpoint?: string;
36
+ /** JWKS endpoint for token validation */
37
+ jwksUri?: string;
38
+ /** Token expiration in seconds */
39
+ tokenExpiration?: number;
40
+ }
41
+
42
+ export interface TokenPayload {
43
+ /** Subject (user/client ID) */
44
+ sub: string;
45
+ /** Issuer */
46
+ iss: string;
47
+ /** Audience (this MCP server) */
48
+ aud: string;
49
+ /** Expiration timestamp */
50
+ exp: number;
51
+ /** Issued at timestamp */
52
+ iat: number;
53
+ /** Scopes granted */
54
+ scope: string;
55
+ /** Client ID */
56
+ client_id: string;
57
+ }
58
+
59
+ export interface AuthResult {
60
+ valid: boolean;
61
+ payload?: TokenPayload;
62
+ error?: string;
63
+ scopes?: string[];
64
+ }
65
+
66
+ export interface PKCEChallenge {
67
+ codeVerifier: string;
68
+ codeChallenge: string;
69
+ codeChallengeMethod: "S256";
70
+ }
71
+
72
+ // ============================================================================
73
+ // MCP Scopes Definition
74
+ // ============================================================================
75
+
76
+ export const MCP_SCOPES = {
77
+ // Read-only scopes
78
+ "read:project": "Read project context and structure",
79
+ "read:graph": "Read dependency graph",
80
+ "read:search": "Execute search queries",
81
+
82
+ // Write scopes
83
+ "write:sync": "Trigger sync operations",
84
+ "write:cache": "Modify cache",
85
+
86
+ // Execute scopes
87
+ "execute:tools": "Execute MCP tools",
88
+ "execute:analysis": "Run code analysis",
89
+
90
+ // Admin scopes
91
+ "admin:config": "Modify server configuration",
92
+ "admin:users": "Manage user access",
93
+ "admin:*": "Full administrative access",
94
+ } as const;
95
+
96
+ export type MCPScope = keyof typeof MCP_SCOPES;
97
+
98
+ // ============================================================================
99
+ // PKCE Helpers
100
+ // ============================================================================
101
+
102
+ /**
103
+ * Generate PKCE code verifier and challenge
104
+ */
105
+ export function generatePKCE(): PKCEChallenge {
106
+ // Generate 32-byte random verifier (43 chars base64url)
107
+ const codeVerifier = crypto.randomBytes(32)
108
+ .toString("base64url")
109
+ .replace(/[^a-zA-Z0-9]/g, "")
110
+ .substring(0, 43);
111
+
112
+ // SHA256 hash of verifier, base64url encoded
113
+ const codeChallenge = crypto
114
+ .createHash("sha256")
115
+ .update(codeVerifier)
116
+ .digest("base64url");
117
+
118
+ return {
119
+ codeVerifier,
120
+ codeChallenge,
121
+ codeChallengeMethod: "S256",
122
+ };
123
+ }
124
+
125
+ /**
126
+ * Verify PKCE code challenge
127
+ */
128
+ export function verifyPKCE(codeVerifier: string, codeChallenge: string): boolean {
129
+ const computed = crypto
130
+ .createHash("sha256")
131
+ .update(codeVerifier)
132
+ .digest("base64url");
133
+
134
+ return crypto.timingSafeEqual(
135
+ Buffer.from(computed),
136
+ Buffer.from(codeChallenge)
137
+ );
138
+ }
139
+
140
+ // ============================================================================
141
+ // Token Management
142
+ // ============================================================================
143
+
144
+ // In-memory token store (replace with Redis/DB in production)
145
+ const tokenStore = new Map<string, TokenPayload>();
146
+ const authCodeStore = new Map<string, {
147
+ clientId: string;
148
+ scopes: string[];
149
+ codeChallenge: string;
150
+ expiresAt: number;
151
+ }>();
152
+
153
+ /**
154
+ * Generate access token
155
+ */
156
+ export function generateAccessToken(
157
+ config: OAuthConfig,
158
+ clientId: string,
159
+ scopes: string[],
160
+ subject: string = clientId
161
+ ): string {
162
+ const now = Math.floor(Date.now() / 1000);
163
+ const expiration = config.tokenExpiration || 3600; // 1 hour default
164
+
165
+ const payload: TokenPayload = {
166
+ sub: subject,
167
+ iss: config.issuer,
168
+ aud: config.clientId,
169
+ exp: now + expiration,
170
+ iat: now,
171
+ scope: scopes.join(" "),
172
+ client_id: clientId,
173
+ };
174
+
175
+ // Generate random token ID
176
+ const tokenId = crypto.randomBytes(32).toString("base64url");
177
+
178
+ // Store token for validation
179
+ tokenStore.set(tokenId, payload);
180
+
181
+ // In production, use JWT with RS256
182
+ // For now, return opaque token
183
+ return tokenId;
184
+ }
185
+
186
+ /**
187
+ * Validate access token
188
+ */
189
+ export function validateAccessToken(token: string): AuthResult {
190
+ const payload = tokenStore.get(token);
191
+
192
+ if (!payload) {
193
+ return { valid: false, error: "Invalid token" };
194
+ }
195
+
196
+ const now = Math.floor(Date.now() / 1000);
197
+ if (payload.exp < now) {
198
+ tokenStore.delete(token);
199
+ return { valid: false, error: "Token expired" };
200
+ }
201
+
202
+ return {
203
+ valid: true,
204
+ payload,
205
+ scopes: payload.scope.split(" "),
206
+ };
207
+ }
208
+
209
+ /**
210
+ * Revoke access token
211
+ */
212
+ export function revokeAccessToken(token: string): boolean {
213
+ return tokenStore.delete(token);
214
+ }
215
+
216
+ // ============================================================================
217
+ // Authorization Code Flow
218
+ // ============================================================================
219
+
220
+ /**
221
+ * Generate authorization code
222
+ */
223
+ export function generateAuthorizationCode(
224
+ clientId: string,
225
+ scopes: string[],
226
+ codeChallenge: string
227
+ ): string {
228
+ const code = crypto.randomBytes(32).toString("base64url");
229
+
230
+ authCodeStore.set(code, {
231
+ clientId,
232
+ scopes,
233
+ codeChallenge,
234
+ expiresAt: Date.now() + 600000, // 10 minutes
235
+ });
236
+
237
+ return code;
238
+ }
239
+
240
+ /**
241
+ * Exchange authorization code for tokens
242
+ */
243
+ export function exchangeAuthorizationCode(
244
+ config: OAuthConfig,
245
+ code: string,
246
+ clientId: string,
247
+ codeVerifier: string
248
+ ): { accessToken?: string; error?: string } {
249
+ const authCode = authCodeStore.get(code);
250
+
251
+ if (!authCode) {
252
+ return { error: "Invalid authorization code" };
253
+ }
254
+
255
+ if (authCode.expiresAt < Date.now()) {
256
+ authCodeStore.delete(code);
257
+ return { error: "Authorization code expired" };
258
+ }
259
+
260
+ if (authCode.clientId !== clientId) {
261
+ return { error: "Client ID mismatch" };
262
+ }
263
+
264
+ // Verify PKCE
265
+ if (!verifyPKCE(codeVerifier, authCode.codeChallenge)) {
266
+ return { error: "PKCE verification failed" };
267
+ }
268
+
269
+ // Delete used code
270
+ authCodeStore.delete(code);
271
+
272
+ // Generate access token
273
+ const accessToken = generateAccessToken(config, clientId, authCode.scopes);
274
+
275
+ return { accessToken };
276
+ }
277
+
278
+ // ============================================================================
279
+ // Scope Validation
280
+ // ============================================================================
281
+
282
+ /**
283
+ * Check if token has required scope
284
+ */
285
+ export function hasScope(authResult: AuthResult, requiredScope: MCPScope): boolean {
286
+ if (!authResult.valid || !authResult.scopes) {
287
+ return false;
288
+ }
289
+
290
+ // Admin wildcard
291
+ if (authResult.scopes.includes("admin:*")) {
292
+ return true;
293
+ }
294
+
295
+ // Check prefix wildcards (e.g., "read:*" matches "read:project")
296
+ const [category] = requiredScope.split(":");
297
+ if (authResult.scopes.includes(`${category}:*`)) {
298
+ return true;
299
+ }
300
+
301
+ return authResult.scopes.includes(requiredScope);
302
+ }
303
+
304
+ /**
305
+ * Check if token has all required scopes
306
+ */
307
+ export function hasAllScopes(authResult: AuthResult, requiredScopes: MCPScope[]): boolean {
308
+ return requiredScopes.every(scope => hasScope(authResult, scope));
309
+ }
310
+
311
+ /**
312
+ * Check if token has any of the required scopes
313
+ */
314
+ export function hasAnyScope(authResult: AuthResult, requiredScopes: MCPScope[]): boolean {
315
+ return requiredScopes.some(scope => hasScope(authResult, scope));
316
+ }
317
+
318
+ // ============================================================================
319
+ // HTTP Middleware
320
+ // ============================================================================
321
+
322
+ /**
323
+ * Extract bearer token from request
324
+ */
325
+ export function extractBearerToken(req: IncomingMessage): string | null {
326
+ const authHeader = req.headers.authorization;
327
+
328
+ if (!authHeader || !authHeader.startsWith("Bearer ")) {
329
+ return null;
330
+ }
331
+
332
+ return authHeader.substring(7);
333
+ }
334
+
335
+ /**
336
+ * Authentication middleware
337
+ */
338
+ export function createAuthMiddleware(config: OAuthConfig) {
339
+ return function authMiddleware(
340
+ req: IncomingMessage,
341
+ res: ServerResponse,
342
+ next: () => void
343
+ ): void {
344
+ const token = extractBearerToken(req);
345
+
346
+ if (!token) {
347
+ res.writeHead(401, {
348
+ "Content-Type": "application/json",
349
+ "WWW-Authenticate": 'Bearer realm="MCP Server"',
350
+ });
351
+ res.end(JSON.stringify({ error: "Authentication required" }));
352
+ return;
353
+ }
354
+
355
+ const result = validateAccessToken(token);
356
+
357
+ if (!result.valid) {
358
+ res.writeHead(401, {
359
+ "Content-Type": "application/json",
360
+ "WWW-Authenticate": `Bearer realm="MCP Server", error="${result.error}"`,
361
+ });
362
+ res.end(JSON.stringify({ error: result.error }));
363
+ return;
364
+ }
365
+
366
+ // Attach auth result to request for scope checking
367
+ (req as any).auth = result;
368
+ next();
369
+ };
370
+ }
371
+
372
+ /**
373
+ * Scope enforcement middleware
374
+ */
375
+ export function requireScopes(...requiredScopes: MCPScope[]) {
376
+ return function scopeMiddleware(
377
+ req: IncomingMessage,
378
+ res: ServerResponse,
379
+ next: () => void
380
+ ): void {
381
+ const authResult = (req as any).auth as AuthResult | undefined;
382
+
383
+ if (!authResult || !hasAllScopes(authResult, requiredScopes)) {
384
+ res.writeHead(403, { "Content-Type": "application/json" });
385
+ res.end(JSON.stringify({
386
+ error: "Insufficient scope",
387
+ required: requiredScopes,
388
+ granted: authResult?.scopes || [],
389
+ }));
390
+ return;
391
+ }
392
+
393
+ next();
394
+ };
395
+ }
396
+
397
+ // ============================================================================
398
+ // API Key Authentication (Simple Alternative)
399
+ // ============================================================================
400
+
401
+ const apiKeyStore = new Map<string, { scopes: string[]; name: string }>();
402
+
403
+ /**
404
+ * Create API key
405
+ */
406
+ export function createApiKey(name: string, scopes: string[]): string {
407
+ const key = `ak_${crypto.randomBytes(24).toString("base64url")}`;
408
+ apiKeyStore.set(key, { name, scopes });
409
+ return key;
410
+ }
411
+
412
+ /**
413
+ * Validate API key
414
+ */
415
+ export function validateApiKey(key: string): AuthResult {
416
+ const data = apiKeyStore.get(key);
417
+
418
+ if (!data) {
419
+ return { valid: false, error: "Invalid API key" };
420
+ }
421
+
422
+ return {
423
+ valid: true,
424
+ scopes: data.scopes,
425
+ payload: {
426
+ sub: data.name,
427
+ iss: "api-key",
428
+ aud: "mcp-gateway",
429
+ exp: Number.MAX_SAFE_INTEGER,
430
+ iat: Math.floor(Date.now() / 1000),
431
+ scope: data.scopes.join(" "),
432
+ client_id: data.name,
433
+ },
434
+ };
435
+ }
436
+
437
+ /**
438
+ * Extract API key from request (X-API-Key header)
439
+ */
440
+ export function extractApiKey(req: IncomingMessage): string | null {
441
+ return req.headers["x-api-key"] as string || null;
442
+ }
443
+
444
+ export default {
445
+ generatePKCE,
446
+ verifyPKCE,
447
+ generateAccessToken,
448
+ validateAccessToken,
449
+ revokeAccessToken,
450
+ generateAuthorizationCode,
451
+ exchangeAuthorizationCode,
452
+ hasScope,
453
+ hasAllScopes,
454
+ hasAnyScope,
455
+ extractBearerToken,
456
+ createAuthMiddleware,
457
+ requireScopes,
458
+ createApiKey,
459
+ validateApiKey,
460
+ extractApiKey,
461
+ MCP_SCOPES,
462
+ };
@@ -0,0 +1,227 @@
1
+ /**
2
+ * MCP Scope Definitions and Tool-to-Scope Mapping
3
+ *
4
+ * Defines the scope requirements for each MCP tool,
5
+ * enabling fine-grained access control.
6
+ */
7
+
8
+ import { MCPScope } from "./oauth.js";
9
+
10
+ // ============================================================================
11
+ // Tool-to-Scope Mapping
12
+ // ============================================================================
13
+
14
+ /**
15
+ * Maps each tool to its required scopes
16
+ * Tools can require multiple scopes (AND logic)
17
+ */
18
+ export const TOOL_SCOPES: Record<string, MCPScope[]> = {
19
+ // Read-only tools
20
+ "get_project_context": ["read:project"],
21
+ "get_project_intelligence": ["read:project"],
22
+ "analyze_dependencies": ["read:graph"],
23
+ "get_impact_zone": ["read:graph"],
24
+ "search_knowledge": ["read:search"],
25
+ "search_code_logic": ["read:search"],
26
+
27
+ // Write tools
28
+ "force_sync": ["write:sync"],
29
+ "clear_cache": ["write:cache"],
30
+
31
+ // Execution tools
32
+ "execute_analysis": ["execute:analysis"],
33
+
34
+ // Admin tools
35
+ "update_config": ["admin:config"],
36
+ "manage_users": ["admin:users"],
37
+ };
38
+
39
+ /**
40
+ * Default scopes for new clients (minimal access)
41
+ */
42
+ export const DEFAULT_SCOPES: MCPScope[] = [
43
+ "read:project",
44
+ "read:graph",
45
+ "read:search",
46
+ ];
47
+
48
+ /**
49
+ * Full read access scopes
50
+ */
51
+ export const READ_ALL_SCOPES: MCPScope[] = [
52
+ "read:project",
53
+ "read:graph",
54
+ "read:search",
55
+ ];
56
+
57
+ /**
58
+ * Scopes for trusted agents (read + execute)
59
+ */
60
+ export const AGENT_SCOPES: MCPScope[] = [
61
+ "read:project",
62
+ "read:graph",
63
+ "read:search",
64
+ "execute:tools",
65
+ "execute:analysis",
66
+ ];
67
+
68
+ /**
69
+ * Full access scopes (for admin clients)
70
+ */
71
+ export const FULL_ACCESS_SCOPES: MCPScope[] = [
72
+ "read:project",
73
+ "read:graph",
74
+ "read:search",
75
+ "write:sync",
76
+ "write:cache",
77
+ "execute:tools",
78
+ "execute:analysis",
79
+ "admin:config",
80
+ "admin:users",
81
+ ];
82
+
83
+ // ============================================================================
84
+ // Scope Helpers
85
+ // ============================================================================
86
+
87
+ /**
88
+ * Get required scopes for a tool
89
+ */
90
+ export function getRequiredScopes(toolName: string): MCPScope[] {
91
+ return TOOL_SCOPES[toolName] || ["execute:tools"];
92
+ }
93
+
94
+ /**
95
+ * Check if scopes are sufficient for a tool
96
+ */
97
+ export function hasToolAccess(grantedScopes: string[], toolName: string): boolean {
98
+ const required = getRequiredScopes(toolName);
99
+
100
+ // Admin wildcard check
101
+ if (grantedScopes.includes("admin:*")) {
102
+ return true;
103
+ }
104
+
105
+ // Check each required scope
106
+ return required.every(scope => {
107
+ // Direct match
108
+ if (grantedScopes.includes(scope)) {
109
+ return true;
110
+ }
111
+
112
+ // Category wildcard (e.g., "read:*" matches "read:project")
113
+ const [category] = scope.split(":");
114
+ if (grantedScopes.includes(`${category}:*`)) {
115
+ return true;
116
+ }
117
+
118
+ return false;
119
+ });
120
+ }
121
+
122
+ /**
123
+ * Get human-readable scope description
124
+ */
125
+ export function getScopeDescription(scope: MCPScope): string {
126
+ const descriptions: Record<MCPScope, string> = {
127
+ "read:project": "Read project context and structure",
128
+ "read:graph": "Read dependency graph",
129
+ "read:search": "Execute search queries",
130
+ "write:sync": "Trigger sync operations",
131
+ "write:cache": "Modify cache",
132
+ "execute:tools": "Execute MCP tools",
133
+ "execute:analysis": "Run code analysis",
134
+ "admin:config": "Modify server configuration",
135
+ "admin:users": "Manage user access",
136
+ "admin:*": "Full administrative access",
137
+ };
138
+
139
+ return descriptions[scope] || scope;
140
+ }
141
+
142
+ /**
143
+ * Validate scope string format
144
+ */
145
+ export function isValidScope(scope: string): scope is MCPScope {
146
+ const validScopes = Object.keys(TOOL_SCOPES).flatMap(tool => TOOL_SCOPES[tool]);
147
+ const uniqueScopes = [...new Set(validScopes), "admin:*", "read:*", "write:*", "execute:*"];
148
+ return uniqueScopes.includes(scope as MCPScope);
149
+ }
150
+
151
+ /**
152
+ * Parse scope string (space-separated) into array
153
+ */
154
+ export function parseScopes(scopeString: string): MCPScope[] {
155
+ return scopeString
156
+ .split(" ")
157
+ .filter(s => s.length > 0)
158
+ .filter(isValidScope);
159
+ }
160
+
161
+ /**
162
+ * Serialize scopes to string
163
+ */
164
+ export function serializeScopes(scopes: MCPScope[]): string {
165
+ return scopes.join(" ");
166
+ }
167
+
168
+ // ============================================================================
169
+ // Scope Request Helpers
170
+ // ============================================================================
171
+
172
+ /**
173
+ * Get minimum scopes needed for a set of tools
174
+ */
175
+ export function getScopesForTools(toolNames: string[]): MCPScope[] {
176
+ const allScopes = toolNames.flatMap(tool => getRequiredScopes(tool));
177
+ return [...new Set(allScopes)]; // deduplicate
178
+ }
179
+
180
+ /**
181
+ * Check if scope request is within allowed limits
182
+ */
183
+ export function validateScopeRequest(
184
+ requestedScopes: MCPScope[],
185
+ allowedScopes: MCPScope[]
186
+ ): { valid: boolean; denied: MCPScope[] } {
187
+ const denied = requestedScopes.filter(scope => {
188
+ // Admin wildcard allows all
189
+ if (allowedScopes.includes("admin:*")) {
190
+ return false;
191
+ }
192
+
193
+ // Direct match
194
+ if (allowedScopes.includes(scope)) {
195
+ return false;
196
+ }
197
+
198
+ // Category wildcard
199
+ const [category] = scope.split(":");
200
+ if (allowedScopes.includes(`${category}:*` as MCPScope)) {
201
+ return false;
202
+ }
203
+
204
+ return true;
205
+ });
206
+
207
+ return {
208
+ valid: denied.length === 0,
209
+ denied,
210
+ };
211
+ }
212
+
213
+ export default {
214
+ TOOL_SCOPES,
215
+ DEFAULT_SCOPES,
216
+ READ_ALL_SCOPES,
217
+ AGENT_SCOPES,
218
+ FULL_ACCESS_SCOPES,
219
+ getRequiredScopes,
220
+ hasToolAccess,
221
+ getScopeDescription,
222
+ isValidScope,
223
+ parseScopes,
224
+ serializeScopes,
225
+ getScopesForTools,
226
+ validateScopeRequest,
227
+ };