@kya-os/create-mcpi-app 1.7.38-canary.1 → 1.7.38

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 (67) hide show
  1. package/.turbo/turbo-build.log +4 -0
  2. package/.turbo/turbo-test$colon$coverage.log +755 -0
  3. package/.turbo/turbo-test.log +200 -0
  4. package/dist/helpers/fetch-cloudflare-mcpi-template.d.ts.map +1 -1
  5. package/dist/helpers/fetch-cloudflare-mcpi-template.js +43 -912
  6. package/dist/helpers/fetch-cloudflare-mcpi-template.js.map +1 -1
  7. package/dist/utils/fetch-remote-config.d.ts.map +1 -1
  8. package/dist/utils/fetch-remote-config.js +2 -2
  9. package/dist/utils/fetch-remote-config.js.map +1 -1
  10. package/package/package.json +77 -0
  11. package/package.json +1 -1
  12. package/ARCHITECTURE_ANALYSIS.md +0 -392
  13. package/CHANGELOG.md +0 -372
  14. package/DEPRECATION_WARNINGS_ANALYSIS.md +0 -192
  15. package/IMPLEMENTATION_SUMMARY.md +0 -108
  16. package/REMEDIATION_PLAN.md +0 -99
  17. package/dist/.tsbuildinfo +0 -1
  18. package/scripts/prepare-pack.js +0 -47
  19. package/scripts/validate-no-workspace.js +0 -79
  20. package/src/__tests__/cloudflare-template.test.ts +0 -490
  21. package/src/__tests__/helpers/fetch-cloudflare-mcpi-template.test.ts +0 -337
  22. package/src/__tests__/helpers/generate-config.test.ts +0 -312
  23. package/src/__tests__/helpers/generate-identity.test.ts +0 -271
  24. package/src/__tests__/helpers/install.test.ts +0 -370
  25. package/src/__tests__/helpers/validate-project-structure.test.ts +0 -467
  26. package/src/__tests__.bak/regression.test.ts +0 -434
  27. package/src/effects/index.ts +0 -80
  28. package/src/helpers/__tests__/config-builder.spec.ts +0 -231
  29. package/src/helpers/apply-identity-preset.ts +0 -209
  30. package/src/helpers/config-builder.ts +0 -165
  31. package/src/helpers/copy-template.ts +0 -11
  32. package/src/helpers/create.ts +0 -239
  33. package/src/helpers/fetch-cloudflare-mcpi-template.ts +0 -2393
  34. package/src/helpers/fetch-cloudflare-template.ts +0 -361
  35. package/src/helpers/fetch-mcpi-template.ts +0 -236
  36. package/src/helpers/fetch-xmcp-template.ts +0 -153
  37. package/src/helpers/generate-config.ts +0 -118
  38. package/src/helpers/generate-identity.ts +0 -163
  39. package/src/helpers/identity-manager.ts +0 -186
  40. package/src/helpers/install.ts +0 -79
  41. package/src/helpers/rename.ts +0 -17
  42. package/src/helpers/validate-project-structure.ts +0 -127
  43. package/src/index.ts +0 -520
  44. package/src/utils/__tests__/fetch-remote-config.test.ts +0 -271
  45. package/src/utils/check-node.ts +0 -17
  46. package/src/utils/fetch-remote-config.ts +0 -179
  47. package/src/utils/is-folder-empty.ts +0 -60
  48. package/src/utils/validate-project-name.ts +0 -132
  49. package/test-cloudflare/README.md +0 -164
  50. package/test-cloudflare/package.json +0 -28
  51. package/test-cloudflare/src/index.ts +0 -341
  52. package/test-cloudflare/src/tools/greet.ts +0 -19
  53. package/test-cloudflare/tests/cache-invalidation.test.ts +0 -410
  54. package/test-cloudflare/tests/cors-security.test.ts +0 -349
  55. package/test-cloudflare/tests/delegation.test.ts +0 -335
  56. package/test-cloudflare/tests/do-routing.test.ts +0 -314
  57. package/test-cloudflare/tests/integration.test.ts +0 -205
  58. package/test-cloudflare/tests/session-management.test.ts +0 -359
  59. package/test-cloudflare/tsconfig.json +0 -16
  60. package/test-cloudflare/vitest.config.ts +0 -9
  61. package/test-cloudflare/wrangler.toml +0 -37
  62. package/test-node/README.md +0 -44
  63. package/test-node/package.json +0 -23
  64. package/test-node/src/tools/greet.ts +0 -25
  65. package/test-node/xmcp.config.ts +0 -20
  66. package/tsconfig.json +0 -26
  67. package/vitest.config.ts +0 -14
@@ -61,7 +61,7 @@ export async function fetchCloudflareMcpiTemplate(projectPath, options = {}) {
61
61
  zod: "^3.25.76",
62
62
  },
63
63
  devDependencies: {
64
- "@cloudflare/workers-types": "^4.20240925.0",
64
+ "@cloudflare/workers-types": "^4.20251109.0",
65
65
  "@kya-os/create-mcpi-app": "^1.7.23",
66
66
  "@vitest/coverage-v8": "^3.2.4",
67
67
  miniflare: "^3.0.0",
@@ -659,39 +659,6 @@ describe('Security Configuration', () => {
659
659
  });
660
660
  `;
661
661
  fs.writeFileSync(path.join(testsDir, "security.test.ts"), securityTestContent);
662
- // Create vitest config file
663
- const vitestConfigContent = `import { defineConfig } from 'vitest/config';
664
-
665
- export default defineConfig({
666
- test: {
667
- environment: 'miniflare',
668
- environmentOptions: {
669
- kvNamespaces: [
670
- '${className.toUpperCase()}_NONCE_CACHE',
671
- '${className.toUpperCase()}_PROOF_ARCHIVE',
672
- '${className.toUpperCase()}_IDENTITY_STORAGE',
673
- '${className.toUpperCase()}_DELEGATION_STORAGE',
674
- '${className.toUpperCase()}_TOOL_PROTECTION_KV'
675
- ],
676
- durableObjects: {
677
- ${className.toUpperCase()}_OBJECT: '${pascalClassName}MCP'
678
- }
679
- },
680
- coverage: {
681
- provider: 'v8',
682
- reporter: ['text', 'html'],
683
- exclude: ['node_modules/', 'tests/', '*.config.ts'],
684
- thresholds: {
685
- statements: 80,
686
- branches: 70,
687
- functions: 80,
688
- lines: 80
689
- }
690
- }
691
- }
692
- });
693
- `;
694
- fs.writeFileSync(path.join(projectPath, "vitest.config.ts"), vitestConfigContent);
695
662
  // Create greet tool
696
663
  const greetToolContent = `import { z } from "zod";
697
664
 
@@ -740,12 +707,9 @@ export const greetTool = {
740
707
  `;
741
708
  fs.writeFileSync(path.join(toolsDir, "greet.ts"), greetToolContent);
742
709
  // Create mcpi-runtime-config.ts for AgentShield integration
743
- const runtimeConfigContent = `import type { MCPIConfig } from '@kya-os/contracts/config';
744
- import type { CloudflareRuntimeConfig } from '@kya-os/mcp-i-cloudflare/config';
710
+ const runtimeConfigContent = `import type { CloudflareRuntimeConfig } from '@kya-os/mcp-i-cloudflare/config';
745
711
  import type { CloudflareEnv } from '@kya-os/mcp-i-cloudflare';
746
- import { buildBaseConfig } from '@kya-os/create-mcpi-app/config-builder';
747
- // Always import CloudflareRuntime for tool protection service creation
748
- import { CloudflareRuntime } from "@kya-os/mcp-i-cloudflare";
712
+ import { defineConfig } from '@kya-os/mcp-i-cloudflare';
749
713
 
750
714
  /**
751
715
  * Runtime configuration for MCP-I server
@@ -761,106 +725,33 @@ import { CloudflareRuntime } from "@kya-os/mcp-i-cloudflare";
761
725
  *
762
726
  * Note: The service fetches tool protection config by agent DID automatically.
763
727
  * No project ID configuration needed!
764
- *
765
- * This config uses the unified MCPIConfig architecture, ensuring the same
766
- * base configuration shape across all platforms (Cloudflare, Node.js, Vercel).
767
728
  */
768
729
  export function getRuntimeConfig(env: CloudflareEnv): CloudflareRuntimeConfig {
769
- // Build base config that works across all platforms
770
- const baseConfig = buildBaseConfig(env);
771
-
772
- // Extend with Cloudflare-specific properties
773
- return {
774
- ...baseConfig,
775
- // Identity configuration
776
- identity: {
777
- ...baseConfig.identity,
778
- enabled: true,
779
- environment: (env.ENVIRONMENT || env.MCPI_ENV || 'development') as 'development' | 'production'
780
- },
781
- // Proofing configuration
782
- proofing: {
783
- enabled: true,
784
- batchQueue: {
785
- destinations: [
786
- {
787
- type: "agentshield" as const,
788
- apiUrl: env.AGENTSHIELD_API_URL || 'https://kya.vouched.id',
789
- apiKey: env.AGENTSHIELD_API_KEY || ''
790
- }
791
- ],
792
- maxBatchSize: 10,
793
- flushIntervalMs: 5000,
794
- maxRetries: 3,
795
- debug: (env.ENVIRONMENT || env.MCPI_ENV || 'development') === 'development'
796
- }
797
- },
798
- // Delegation configuration
799
- delegation: {
800
- ...baseConfig.delegation
801
- },
802
- // Audit configuration
803
- audit: {
804
- ...baseConfig.audit
805
- },
806
- // Cloudflare Workers-specific configuration
807
- workers: {
808
- cpuMs: 50,
809
- memoryMb: 128
810
- },
811
- // KV namespace bindings
812
- kv: env.TOOL_PROTECTION_KV ? [{
813
- name: 'TOOL_PROTECTION_KV',
814
- purpose: 'cache' as const
815
- }] : [],
816
- // Environment variable bindings
730
+ return defineConfig({
731
+ // Only specify overrides - defaults are handled automatically
817
732
  vars: {
818
733
  ENVIRONMENT: env.ENVIRONMENT || env.MCPI_ENV || 'development',
819
- AGENTSHIELD_API_KEY: env.AGENTSHIELD_API_KEY
734
+ AGENTSHIELD_API_KEY: env.AGENTSHIELD_API_KEY,
735
+ AGENTSHIELD_API_URL: env.AGENTSHIELD_API_URL,
820
736
  },
821
- // Tool protection service (Cloudflare-specific)
822
- toolProtection: env.TOOL_PROTECTION_KV && env.AGENTSHIELD_API_KEY ? {
823
- source: 'agentshield' as const,
824
- agentShield: {
825
- apiUrl: env.AGENTSHIELD_API_URL || 'https://kya.vouched.id',
826
- apiKey: env.AGENTSHIELD_API_KEY,
827
- projectId: env.AGENTSHIELD_PROJECT_ID,
828
- cacheTtl: 300000 // 5 minutes
829
- },
830
- fallback: {
831
- greet: {
832
- requiresDelegation: false,
833
- requiredScopes: ['greet:execute']
834
- }
835
- }
836
- } : undefined
837
- } as CloudflareRuntimeConfig;
737
+ // Optional: Enable admin endpoints
738
+ admin: {
739
+ enabled: false, // Set to true and provide ADMIN_API_KEY to enable
740
+ apiKey: env.ADMIN_API_KEY,
741
+ },
742
+ });
838
743
  }
839
744
  `;
840
745
  fs.writeFileSync(path.join(srcDir, "mcpi-runtime-config.ts"), runtimeConfigContent);
841
- // Create main index.ts using McpAgent with MCP-I runtime
842
- const indexContent = `import { McpAgent } from "agents/mcp";
843
- import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
844
- import { createCloudflareRuntime, type CloudflareEnv, KVProofArchive, type DetachedProof, createOAuthCallbackHandler, CloudflareRuntime, type HonoContext } from "@kya-os/mcp-i-cloudflare";
845
- import { DelegationRequiredError } from "@kya-os/mcp-i-core";
846
- import type { ToolProtectionService } from "@kya-os/mcp-i-core";
847
- import { Hono } from "hono";
848
- import { cors } from "hono/cors";
746
+ // Create main index.ts using MCPICloudflareServer
747
+ const indexContent = `import { MCPICloudflareAgent, createMCPIApp, type PrefixedCloudflareEnv } from "@kya-os/mcp-i-cloudflare";
849
748
  import { greetTool } from "./tools/greet";
850
749
  import { getRuntimeConfig } from "./mcpi-runtime-config";
851
- import type { CloudflareRuntimeConfig } from "@kya-os/mcp-i-cloudflare/config";
852
750
 
853
751
  /**
854
752
  * Extended CloudflareEnv with prefixed KV bindings for multi-agent deployments
855
- * This allows multiple agents to share the same Cloudflare account without conflicts
856
753
  */
857
- interface PrefixedCloudflareEnv extends CloudflareEnv {
858
- // Prefixed KV bindings (e.g., MYAGENT_NONCE_CACHE, MYAGENT_PROOF_ARCHIVE)
859
- [key: string]: KVNamespace | string | DurableObjectState | undefined;
860
- // Optional routing configuration
861
- DO_ROUTING_STRATEGY?: string;
862
- DO_SHARD_COUNT?: string;
863
- // Prefixed KV namespaces (dynamically accessed)
754
+ interface MyPrefixedCloudflareEnv extends PrefixedCloudflareEnv {
864
755
  ${className.toUpperCase()}_NONCE_CACHE?: KVNamespace;
865
756
  ${className.toUpperCase()}_PROOF_ARCHIVE?: KVNamespace;
866
757
  ${className.toUpperCase()}_IDENTITY_STORAGE?: KVNamespace;
@@ -868,806 +759,38 @@ interface PrefixedCloudflareEnv extends CloudflareEnv {
868
759
  ${className.toUpperCase()}_TOOL_PROTECTION_KV?: KVNamespace;
869
760
  }
870
761
 
871
- export class ${pascalClassName}MCP extends McpAgent {
872
- server = new McpServer({
873
- name: "${projectName}",
874
- version: "1.0.0"
875
- });
876
-
877
- private mcpiRuntime?: ReturnType<typeof createCloudflareRuntime>;
878
- private proofArchive?: KVProofArchive;
879
- private agentShieldConfig?: { apiUrl: string; apiKey: string };
880
- protected env: PrefixedCloudflareEnv;
881
- private mcpServerUrl?: string;
882
-
883
- constructor(state: DurableObjectState, env: PrefixedCloudflareEnv) {
884
- super(state, env);
885
- this.env = env;
886
-
887
- // Create CloudflareEnv adapter to map prefixed KV bindings to expected names
888
- // This allows multiple agents to be deployed without KV namespace conflicts
889
- const mappedEnv: CloudflareEnv = {
890
- // Map prefixed bindings to standard names expected by createCloudflareRuntime
891
- // Use type assertions since these are guaranteed to exist at runtime
892
- NONCE_CACHE: env.${className.toUpperCase()}_NONCE_CACHE as KVNamespace,
893
- PROOF_ARCHIVE: env.${className.toUpperCase()}_PROOF_ARCHIVE as KVNamespace | undefined,
894
- IDENTITY_STORAGE: env.${className.toUpperCase()}_IDENTITY_STORAGE as KVNamespace | undefined,
895
- DELEGATION_STORAGE: env.${className.toUpperCase()}_DELEGATION_STORAGE as KVNamespace | undefined,
896
- TOOL_PROTECTION_KV: env.${className.toUpperCase()}_TOOL_PROTECTION_KV as KVNamespace | undefined,
897
- // Pass through environment variables unchanged
898
- MCP_IDENTITY_PRIVATE_KEY: env.MCP_IDENTITY_PRIVATE_KEY,
899
- MCP_IDENTITY_PUBLIC_KEY: env.MCP_IDENTITY_PUBLIC_KEY,
900
- MCP_IDENTITY_AGENT_DID: env.MCP_IDENTITY_AGENT_DID,
901
- // Pass through other env vars for runtime config
902
- AGENTSHIELD_API_URL: env.AGENTSHIELD_API_URL,
903
- AGENTSHIELD_API_KEY: env.AGENTSHIELD_API_KEY,
904
- AGENTSHIELD_PROJECT_ID: env.AGENTSHIELD_PROJECT_ID,
905
- MCPI_ENV: env.MCPI_ENV,
906
- MCP_SERVER_URL: env.MCP_SERVER_URL,
907
- // Pass Durable Object state for identity persistence
908
- // NOTE: Without this, identity will be ephemeral (new DID every call)!
909
- // Note: createCloudflareRuntime will automatically use KVIdentityProvider if IDENTITY_STORAGE KV is available
910
- _durableObjectState: state,
911
- };
912
-
913
- // Store MCP server URL for proof submission context
914
- // Auto-detect from request URL if not explicitly set (will be set in fetch handler)
915
- this.mcpServerUrl = env.MCP_SERVER_URL;
916
- if (this.mcpServerUrl) {
917
- // Ensure URL includes /mcp path if not already present
918
- if (!this.mcpServerUrl.endsWith('/mcp')) {
919
- // Remove trailing slash if present, then append /mcp
920
- const urlWithoutTrailingSlash = this.mcpServerUrl.endsWith('/')
921
- ? this.mcpServerUrl.slice(0, -1)
922
- : this.mcpServerUrl;
923
- this.mcpServerUrl = urlWithoutTrailingSlash + '/mcp';
924
- }
925
- console.log('[MCP-I] MCP Server URL configured:', this.mcpServerUrl);
926
- } else {
927
- // Will be auto-detected from request URL in fetch handler
928
- console.log('[MCP-I] MCP Server URL will be auto-detected from request');
929
- }
930
-
931
- // Load runtime configuration for AgentShield integration
932
- // Pass mappedEnv so it can access KV bindings with standard names
933
- const runtimeConfig = getRuntimeConfig(mappedEnv);
934
-
935
- // ✅ Create tool protection service helper function
936
- // Always import CloudflareRuntime but conditionally instantiate
937
- function createToolProtectionService(env: CloudflareEnv, runtimeConfig: CloudflareRuntimeConfig): ToolProtectionService | undefined {
938
- if (!runtimeConfig.toolProtection) {
939
- return undefined;
940
- }
941
-
942
- if (!env.TOOL_PROTECTION_KV || !env.AGENTSHIELD_API_KEY) {
943
- console.log('[MCP-I] Tool protection disabled - configure TOOL_PROTECTION_KV and AGENTSHIELD_API_KEY to enable');
944
- return undefined;
945
- }
946
-
947
- const apiKey = env.AGENTSHIELD_API_KEY;
948
- if (!apiKey) {
949
- console.log('[MCP-I] Tool protection disabled - AGENTSHIELD_API_KEY not configured');
950
- return undefined;
951
- }
952
-
953
- const toolProtectionConfig: import('@kya-os/mcp-i-core').ToolProtectionServiceConfig = {
954
- apiUrl: runtimeConfig.toolProtection.agentShield?.apiUrl || env.AGENTSHIELD_API_URL || 'https://kya.vouched.id',
955
- apiKey: apiKey,
956
- projectId: runtimeConfig.toolProtection.agentShield?.projectId || env.AGENTSHIELD_PROJECT_ID,
957
- cacheTtl: runtimeConfig.toolProtection.agentShield?.cacheTtl || 300000,
958
- debug: runtimeConfig.environment === 'development',
959
- };
960
-
961
- if (runtimeConfig.toolProtection.fallback) {
962
- toolProtectionConfig.fallbackConfig = {
963
- toolProtections: runtimeConfig.toolProtection.fallback
964
- };
965
- }
966
-
967
- return CloudflareRuntime.createToolProtectionService(
968
- env.TOOL_PROTECTION_KV!,
969
- toolProtectionConfig
970
- );
971
- }
972
-
973
- // Create tool protection service if configured
974
- // Note: createCloudflareRuntime will automatically use KVIdentityProvider if IDENTITY_STORAGE KV is available
975
- const toolProtectionService = createToolProtectionService(mappedEnv, runtimeConfig);
976
-
977
- // Initialize MCP-I runtime for cryptographic proofs and identity
978
- this.mcpiRuntime = createCloudflareRuntime({
979
- env: mappedEnv as CloudflareEnv,
980
- environment: runtimeConfig.environment,
981
- audit: {
982
- enabled: runtimeConfig.audit?.enabled ?? true,
983
- logFunction: runtimeConfig.audit?.logFunction || ((record) => console.log('[MCP-I Audit]', record))
984
- },
985
- toolProtectionService
986
- });
987
-
988
- // Initialize proof archive if PROOF_ARCHIVE KV is available
989
- if (mappedEnv.PROOF_ARCHIVE) {
990
- this.proofArchive = new KVProofArchive(mappedEnv.PROOF_ARCHIVE);
991
- console.log('[MCP-I] Proof archive enabled');
992
- }
993
-
994
- // Load AgentShield config for proof submission
995
- if (runtimeConfig.proofing?.enabled && runtimeConfig.proofing.batchQueue) {
996
- const agentShieldDest = runtimeConfig.proofing.batchQueue.destinations?.find(
997
- (dest) => dest.type === "agentshield" && dest.apiKey
998
- );
999
- if (agentShieldDest && agentShieldDest.apiKey && agentShieldDest.apiUrl) {
1000
- this.agentShieldConfig = {
1001
- apiUrl: agentShieldDest.apiUrl,
1002
- apiKey: agentShieldDest.apiKey
1003
- };
1004
- console.log('[MCP-I] AgentShield enabled:', agentShieldDest.apiUrl);
1005
- }
1006
- }
1007
- }
1008
-
1009
- /**
1010
- * Handle incoming requests
1011
- * Auto-detects MCP Server URL from request if not explicitly configured
1012
- */
1013
- async fetch(request: Request): Promise<Response> {
1014
- // Auto-detect MCP Server URL from request if not already set
1015
- if (!this.mcpServerUrl && request.url) {
1016
- try {
1017
- const requestUrl = new URL(request.url);
1018
- // Extract origin and ensure /mcp path is included
1019
- this.mcpServerUrl = requestUrl.origin + '/mcp';
1020
- console.log('[MCP-I] MCP Server URL auto-detected from request:', this.mcpServerUrl);
1021
- } catch (error) {
1022
- console.warn('[MCP-I] Failed to auto-detect MCP Server URL from request:', error);
1023
- }
1024
- }
1025
-
1026
- // Delegate to McpAgent's fetch handler
1027
- return super.fetch(request);
1028
- }
1029
-
1030
- /**
1031
- * Override getInstanceId() to enable multi-instance Durable Object routing
1032
- *
1033
- * This method is called internally by McpAgent to determine which DO instance
1034
- * should handle the request. By overriding it, we can implement custom routing
1035
- * strategies (session-based, shard-based, etc.) while maintaining full
1036
- * McpAgent compatibility and preserving PartyServer routing context.
1037
- *
1038
- * @returns Instance ID used by McpAgent for DO routing
1039
- */
1040
- getInstanceId(): string {
1041
- try {
1042
- // Get session ID from McpAgent's built-in extraction
1043
- const sessionId = this.getSessionId();
1044
-
1045
- // Get routing strategy from environment (default: session)
1046
- const strategy = this.env.DO_ROUTING_STRATEGY || 'session';
1047
-
1048
- if (strategy === 'session') {
1049
- // One DO instance per MCP session (recommended for most use cases)
1050
- // Sessions are isolated, ensuring data consistency per client
1051
- return \`session:\${sessionId}\`;
1052
- } else if (strategy === 'shard') {
1053
- // Hash-based sharding across N DO instances (for high load)
1054
- // Distributes load evenly while maintaining session affinity
1055
- const shardCount = parseInt(this.env.DO_SHARD_COUNT || '10');
1056
- // Validate shard count - must be a valid positive number
1057
- const validShardCount = (!isNaN(shardCount) && shardCount > 0) ? shardCount : 10;
1058
-
1059
- // Simple hash function for session ID
1060
- let hash = 0;
1061
- for (let i = 0; i < sessionId.length; i++) {
1062
- hash = ((hash << 5) - hash) + sessionId.charCodeAt(i);
1063
- hash = hash & hash; // Convert to 32bit integer
1064
- }
1065
-
1066
- const shard = Math.abs(hash) % validShardCount;
1067
- return \`shard:\${shard}\`;
1068
- }
1069
-
1070
- // Fallback to single instance (legacy behavior)
1071
- return 'default';
1072
- } catch (error) {
1073
- // If session extraction fails, fall back to default instance
1074
- console.error('[DO Routing] Failed to extract session ID:', error);
1075
- return 'default';
1076
- }
1077
- }
1078
-
1079
- /**
1080
- * Retrieve delegation token from KV storage
1081
- * Uses two-tier lookup: session cache (fast) → agent DID (stable)
1082
- *
1083
- * @param sessionId - MCP session ID from Claude Desktop
1084
- * @returns Delegation token if found, null otherwise
1085
- */
1086
- private async getDelegationToken(sessionId?: string): Promise<string | null> {
1087
- const delegationStorage = this.env.${className.toUpperCase()}_DELEGATION_STORAGE;
1088
-
1089
- if (!delegationStorage) {
1090
- console.log('[Delegation] No delegation storage configured');
1091
- return null;
1092
- }
1093
-
1094
- try {
1095
- // Fast path: Try session cache first
1096
- if (sessionId) {
1097
- const sessionKey = \`session:\${sessionId}\`;
1098
- const sessionToken = await delegationStorage.get(sessionKey);
1099
-
1100
- if (sessionToken) {
1101
- // Verify token is still valid before returning
1102
- const isValid = await this.verifyDelegationWithAgentShield(sessionToken);
1103
- if (isValid) {
1104
- console.log('[Delegation] ✅ Token retrieved from session cache and verified');
1105
- return sessionToken;
1106
- } else {
1107
- // Token invalid, remove from cache
1108
- await this.invalidateDelegationCache(sessionId, sessionToken);
1109
- console.log('[Delegation] ⚠️ Cached token was invalid, removed from cache');
1110
- }
1111
- }
1112
- }
1113
-
1114
- // Fallback: Try agent DID (stable across session changes)
1115
- if (this.mcpiRuntime) {
1116
- const identity = await this.mcpiRuntime.getIdentity();
1117
- if (identity?.did) {
1118
- const agentKey = \`agent:\${identity.did}:delegation\`;
1119
- const agentToken = await delegationStorage.get(agentKey);
1120
-
1121
- if (agentToken) {
1122
- // Verify token is still valid before returning
1123
- const isValid = await this.verifyDelegationWithAgentShield(agentToken);
1124
- if (isValid) {
1125
- console.log('[Delegation] ✅ Token retrieved using agent DID and verified');
1126
-
1127
- // Re-cache for current session (performance optimization)
1128
- if (sessionId) {
1129
- const sessionCacheKey = \`session:\${sessionId}\`;
1130
- await delegationStorage.put(sessionCacheKey, agentToken, {
1131
- expirationTtl: 300 // 5 minutes for security (reduced from 30)
1132
- });
1133
- console.log('[Delegation] Token cached for session with 5-minute TTL:', sessionId);
1134
- }
1135
-
1136
- return agentToken;
1137
- } else {
1138
- // Token invalid, remove from cache
1139
- await this.invalidateDelegationCache(sessionId, agentToken, identity.did);
1140
- console.log('[Delegation] ⚠️ Agent token was invalid, removed from cache');
1141
- }
1142
- }
1143
- }
1144
- }
1145
-
1146
- console.log('[Delegation] No delegation token found');
1147
- return null;
1148
- } catch (error) {
1149
- console.error('[Delegation] Failed to retrieve token:', error);
1150
- return null;
1151
- }
1152
- }
1153
-
1154
- /**
1155
- * Verify delegation token with AgentShield API
1156
- * @param token - Delegation token to verify
1157
- * @returns True if token is valid, false otherwise
1158
- */
1159
- private async verifyDelegationWithAgentShield(token: string): Promise<boolean> {
1160
- // Check verification cache first (1 minute TTL for verified tokens)
1161
- const verificationCache = this.env.TOOL_PROTECTION_KV;
1162
- if (verificationCache) {
1163
- const cacheKey = \`verified:\${token.substring(0, 16)}\`; // Use prefix to avoid key size issues
1164
- const cached = await verificationCache.get(cacheKey);
1165
- if (cached === '1') {
1166
- console.log('[Delegation] Token verification cached as valid');
1167
- return true;
1168
- }
1169
- }
1170
-
1171
- try {
1172
- const agentShieldUrl = this.env.AGENTSHIELD_API_URL || 'https://kya.vouched.id';
1173
- const apiKey = this.env.AGENTSHIELD_API_KEY;
1174
-
1175
- if (!apiKey) {
1176
- console.warn('[Delegation] No AgentShield API key configured, skipping verification');
1177
- return true; // Allow in development without API key
1178
- }
1179
-
1180
- // Verify with AgentShield API
1181
- const response = await fetch(\`\${agentShieldUrl}/api/v1/bouncer/delegations/verify\`, {
1182
- method: 'POST',
1183
- headers: {
1184
- 'Authorization': \`Bearer \${apiKey}\`,
1185
- 'Content-Type': 'application/json'
1186
- },
1187
- body: JSON.stringify({ token })
1188
- });
1189
-
1190
- if (response.ok) {
1191
- // Cache successful verification for 1 minute
1192
- if (verificationCache) {
1193
- const cacheKey = \`verified:\${token.substring(0, 16)}\`;
1194
- await verificationCache.put(cacheKey, '1', {
1195
- expirationTtl: 60 // 1 minute cache for verified tokens
1196
- });
1197
- }
1198
- console.log('[Delegation] Token verified successfully with AgentShield');
1199
- return true;
1200
- }
1201
-
1202
- if (response.status === 401 || response.status === 403) {
1203
- console.log('[Delegation] Token verification failed: unauthorized');
1204
- return false;
1205
- }
1206
-
1207
- console.warn('[Delegation] Token verification returned unexpected status:', response.status);
1208
- return false; // Fail closed for security
1209
-
1210
- } catch (error) {
1211
- console.error('[Delegation] Error verifying token with AgentShield:', error);
1212
- return false; // Fail closed on errors
1213
- }
1214
- }
1215
-
1216
- /**
1217
- * Invalidate delegation token in all caches
1218
- * @param sessionId - Session ID to clear
1219
- * @param token - Token to invalidate
1220
- * @param agentDid - Agent DID to clear
1221
- */
1222
- private async invalidateDelegationCache(sessionId?: string, token?: string, agentDid?: string): Promise<void> {
1223
- const delegationStorage = this.env.${className.toUpperCase()}_DELEGATION_STORAGE;
1224
- const verificationCache = this.env.TOOL_PROTECTION_KV;
1225
-
1226
- if (!delegationStorage) return;
1227
-
1228
- const deletions: Promise<void>[] = [];
1229
-
1230
- // Clear session cache
1231
- if (sessionId) {
1232
- const sessionKey = \`session:\${sessionId}\`;
1233
- deletions.push(delegationStorage.delete(sessionKey));
1234
- }
1235
-
1236
- // Clear agent cache
1237
- if (agentDid) {
1238
- const agentKey = \`agent:\${agentDid}:delegation\`;
1239
- deletions.push(delegationStorage.delete(agentKey));
1240
- }
1241
-
1242
- // Clear verification cache
1243
- if (token && verificationCache) {
1244
- const cacheKey = \`verified:\${token.substring(0, 16)}\`;
1245
- deletions.push(verificationCache.delete(cacheKey));
1246
- }
1247
-
1248
- await Promise.all(deletions);
1249
- console.log('[Delegation] Cache invalidated for revoked/invalid token');
1250
- }
1251
-
1252
- /**
1253
- * Submit proof to AgentShield API
1254
- * Uses the proof.jws directly (full JWS format from CloudflareRuntime)
1255
- *
1256
- * Also submits optional context for AgentShield dashboard integration.
1257
- * Context provides plaintext tool/args data while proof provides cryptographic verification.
1258
- */
1259
- private async submitProofToAgentShield(
1260
- proof: DetachedProof,
1261
- session: { id: string },
1262
- toolName: string,
1263
- args: Record<string, unknown>,
1264
- result: unknown
1265
- ): Promise<void> {
1266
- if (!this.agentShieldConfig || !proof.jws || !proof.meta) return;
1267
-
1268
- const { apiUrl, apiKey } = this.agentShieldConfig;
1269
-
1270
- // Get tool call context from runtime (if available)
1271
- const toolCallContext = this.mcpiRuntime?.getLastToolCallContext();
1272
-
1273
- // Proof already has correct format from CloudflareRuntime
1274
- // Adding optional context for AgentShield dashboard (Option A architecture)
1275
- const requestBody = {
1276
- session_id: session.id,
1277
- delegation_id: null,
1278
- proofs: [{
1279
- jws: proof.jws, // Already in full JWS format
1280
- meta: proof.meta // Already has all required fields
1281
- }],
1282
- // ✅ NEW: Optional context for dashboard integration
1283
- context: {
1284
- toolCalls: toolCallContext ? [toolCallContext] : [{
1285
- // Fallback if context not available from runtime
1286
- tool: toolName,
1287
- args: args,
1288
- result: result,
1289
- scopeId: proof.meta.scopeId || \`\${toolName}:execute\`
1290
- }],
1291
- // ✅ NEW: MCP server URL for tool discovery (optional, only needed once)
1292
- mcpServerUrl: this.mcpServerUrl
1293
- }
1294
- };
1295
-
1296
- console.log('[AgentShield] Submitting proof with context:', {
1297
- did: proof.meta.did,
1298
- sessionId: proof.meta.sessionId,
1299
- jwsFormat: proof.jws.split('.').length === 3 ? 'valid (3 parts)' : 'invalid',
1300
- contextTool: requestBody.context.toolCalls[0]?.tool,
1301
- contextScopeId: requestBody.context.toolCalls[0]?.scopeId,
1302
- mcpServerUrl: requestBody.context.mcpServerUrl || 'not-set'
1303
- });
1304
-
1305
- const response = await fetch(\`\${apiUrl}/api/v1/bouncer/proofs\`, {
1306
- method: 'POST',
1307
- headers: {
1308
- 'Content-Type': 'application/json',
1309
- 'Authorization': \`Bearer \${apiKey}\`
1310
- },
1311
- body: JSON.stringify(requestBody)
1312
- });
1313
-
1314
- if (!response.ok) {
1315
- const errorText = await response.text();
1316
- console.error('[AgentShield] Submission failed:', response.status, errorText);
1317
- throw new Error(\`AgentShield error: \${response.status}\`);
1318
- }
1319
-
1320
- const responseData = await response.json() as { success?: boolean; received?: number; processed?: number; accepted?: number; rejected?: number; errors?: Array<{ proofId: string; error: string }> };
1321
- console.log('[AgentShield] Response:', responseData);
1322
-
1323
- if (responseData.accepted) {
1324
- console.log('[AgentShield] ✅ Proofs accepted:', responseData.accepted);
1325
- }
1326
- if (responseData.rejected) {
1327
- console.log('[AgentShield] ❌ Proofs rejected:', responseData.rejected);
1328
- }
1329
- }
1330
-
1331
- async init() {
1332
- // Initialize MCP-I runtime (generates/loads identity, sets up nonce cache)
1333
- await this.mcpiRuntime?.initialize();
1334
-
1335
- const identity = await this.mcpiRuntime?.getIdentity();
1336
- console.log('[MCP-I] Initialized with DID:', identity?.did);
1337
-
762
+ /**
763
+ * Your custom MCP agent - only define your tools here!
764
+ * All framework complexity (runtime initialization, proof generation, etc.) is handled automatically.
765
+ */
766
+ export class ${pascalClassName}MCP extends MCPICloudflareAgent {
767
+ async registerTools() {
768
+ // Register your custom tools - proofs are generated automatically
1338
769
  this.server.tool(
1339
770
  greetTool.name,
1340
771
  greetTool.description,
1341
772
  greetTool.inputSchema.shape,
1342
773
  async (args: { name: string }) => {
1343
- // Use MCP-I runtime's processToolCall for automatic proof generation
1344
- if (this.mcpiRuntime) {
1345
- try {
1346
- // Read MCP session ID from Claude Desktop (via agents framework)
1347
- let mcpSessionId: string | undefined;
1348
- try {
1349
- mcpSessionId = this.getSessionId();
1350
- console.log('[Delegation] Session ID from agents framework:', mcpSessionId);
1351
- } catch (error) {
1352
- console.log('[Delegation] Failed to get session ID from framework:', error);
1353
- mcpSessionId = undefined;
1354
- }
1355
-
1356
- // Retrieve delegation token if available
1357
- const delegationToken = await this.getDelegationToken(mcpSessionId);
1358
-
1359
- // Create session with proper ID (use actual session ID when available)
1360
- const timestamp = Date.now();
1361
- const sessionId = mcpSessionId || \`ephemeral-\${timestamp}-\${Math.random().toString(36).substring(2, 10)}\`;
1362
-
1363
- const session = {
1364
- id: sessionId, // Use actual session ID from Claude Desktop
1365
- audience: 'https://kya.vouched.id', // CRITICAL: Must match AgentShield domain
1366
- agentDid: (await this.mcpiRuntime.getIdentity()).did,
1367
- createdAt: timestamp,
1368
- expiresAt: timestamp + (30 * 60 * 1000), // 30 minutes
1369
- delegationToken // Include delegation token if available
1370
- };
1371
-
1372
- // Execute tool with automatic proof generation
1373
- const result = await this.mcpiRuntime.processToolCall(
1374
- greetTool.name,
1375
- args,
1376
- greetTool.handler,
1377
- session
1378
- );
1379
-
1380
- // Get proof in DetachedProof format
1381
- const proof = this.mcpiRuntime.getLastProof() as DetachedProof;
1382
-
1383
- if (proof && proof.jws && proof.meta) {
1384
- // Log proof details (using DetachedProof format)
1385
- console.log('[MCP-I Proof]', {
1386
- tool: greetTool.name,
1387
- did: proof.meta.did,
1388
- timestamp: proof.meta.ts,
1389
- jws: proof.jws.substring(0, 50) + '...',
1390
- jwsValid: proof.jws.split('.').length === 3
1391
- });
1392
-
1393
- // Parallelize proof operations for better performance
1394
- const proofOperations: Promise<void>[] = [];
1395
-
1396
- // Add proof archive operation
1397
- if (this.proofArchive) {
1398
- proofOperations.push(
1399
- this.proofArchive.store(proof, {
1400
- toolName: greetTool.name
1401
- }).then(() => {
1402
- console.log('[MCP-I] Proof stored in archive');
1403
- }).catch((archiveError: unknown) => {
1404
- console.error('[MCP-I] Archive error:', archiveError instanceof Error ? archiveError.message : String(archiveError));
1405
- })
1406
- );
1407
- }
1408
-
1409
- // Add AgentShield submission operation
1410
- if (this.agentShieldConfig) {
1411
- proofOperations.push(
1412
- this.submitProofToAgentShield(proof, session, greetTool.name, args, result)
1413
- .catch((err: unknown) => {
1414
- console.error('[MCP-I] AgentShield failed:', err instanceof Error ? err.message : String(err));
1415
- })
1416
- );
1417
- }
1418
-
1419
- // Execute all proof operations in parallel for better performance
1420
- if (proofOperations.length > 0) {
1421
- await Promise.allSettled(proofOperations);
1422
- }
1423
-
1424
- // Attach proof to result for MCP Inspector
1425
- if (result && typeof result === 'object' && result !== null) {
1426
- (result as Record<string, unknown>)._meta = {
1427
- proof: {
1428
- jws: proof.jws,
1429
- did: proof.meta.did,
1430
- kid: proof.meta.kid,
1431
- timestamp: proof.meta.ts,
1432
- nonce: proof.meta.nonce,
1433
- sessionId: proof.meta.sessionId,
1434
- requestHash: proof.meta.requestHash,
1435
- responseHash: proof.meta.responseHash
1436
- }
1437
- };
1438
- }
1439
- }
1440
-
1441
- return result;
1442
- } catch (error: unknown) {
1443
- // If this is a DelegationRequiredError, re-throw it so the MCP framework can handle it properly
1444
- // The agents/mcp framework will format it as a proper error response to Claude Desktop
1445
- if (error instanceof DelegationRequiredError) {
1446
- console.warn('[MCP-I] Delegation required, propagating error:', {
1447
- tool: error.toolName,
1448
- requiredScopes: error.requiredScopes,
1449
- consentUrl: error.consentUrl
1450
- });
1451
- throw error;
1452
- }
1453
- // Check for DelegationRequiredError by name (for cases where error is not instanceof)
1454
- if (error && typeof error === 'object' && 'name' in error && error.name === 'DelegationRequiredError') {
1455
- const delegationError = error as DelegationRequiredError;
1456
- console.warn('[MCP-I] Delegation required, propagating error:', {
1457
- tool: delegationError.toolName,
1458
- requiredScopes: delegationError.requiredScopes,
1459
- consentUrl: delegationError.consentUrl
1460
- });
1461
- throw error;
1462
- }
1463
-
1464
- // For other errors, log and fallback to direct execution
1465
- console.error('[MCP-I] Failed to process tool with runtime:', error);
1466
- // Fallback to direct execution
1467
- return await greetTool.handler(args);
1468
- }
1469
- }
1470
-
1471
- // Fallback if runtime not available
1472
- return await greetTool.handler(args);
774
+ return this.executeToolWithProof(
775
+ greetTool.name,
776
+ args,
777
+ greetTool.handler
778
+ );
1473
779
  }
1474
780
  );
1475
781
  }
1476
782
  }
1477
783
 
1478
- const app = new Hono();
1479
-
1480
- // Secure CORS configuration
1481
- app.use("/*", (c, next) => {
1482
- const env = c.env as PrefixedCloudflareEnv;
1483
- // Ensure ALLOWED_ORIGINS is a string, not a KVNamespace or DurableObjectState
1484
- const allowedOriginsStr = typeof env.ALLOWED_ORIGINS === 'string' ? env.ALLOWED_ORIGINS : undefined;
1485
- const allowedOrigins = allowedOriginsStr?.split(',').map((o: string) => o.trim()) || [
1486
- 'https://claude.ai',
1487
- 'https://app.anthropic.com'
1488
- ];
1489
-
1490
- // Add localhost for development if not in production
1491
- const mcpiEnv = typeof env.MCPI_ENV === 'string' ? env.MCPI_ENV : undefined;
1492
- if (mcpiEnv !== 'production' && !allowedOrigins.includes('http://localhost:3000')) {
1493
- allowedOrigins.push('http://localhost:3000');
1494
- }
1495
-
1496
- const origin = c.req.header('Origin') || '';
1497
- const isAllowed = allowedOrigins.includes(origin);
1498
-
1499
- return cors({
1500
- origin: isAllowed ? origin : allowedOrigins[0], // Default to first allowed origin if not matched
1501
- allowMethods: ["GET", "POST", "PUT", "DELETE", "OPTIONS"],
1502
- allowHeaders: ["Content-Type", "Authorization", "mcp-session-id", "Mcp-Session-Id", "mcp-protocol-version"],
1503
- exposeHeaders: ["mcp-session-id", "Mcp-Session-Id"],
1504
- credentials: true,
1505
- })(c, next);
784
+ // Create and export the app - all routing is handled automatically
785
+ export default createMCPIApp({
786
+ AgentClass: ${pascalClassName}MCP,
787
+ agentName: "${projectName}",
788
+ agentVersion: "1.0.0",
789
+ envPrefix: "${className.toUpperCase()}",
790
+ getRuntimeConfig,
1506
791
  });
1507
-
1508
- app.get("/health", (c) => c.json({
1509
- status: 'healthy',
1510
- timestamp: new Date().toISOString(),
1511
- transport: { sse: '/sse', streamableHttp: '/mcp' }
1512
- }));
1513
-
1514
- /**
1515
- * Admin endpoint to clear tool protection cache
1516
- *
1517
- * This allows AgentShield dashboard to invalidate cached tool protection
1518
- * config immediately after changing delegation requirements.
1519
- *
1520
- * The API key is validated by making a test call to AgentShield API.
1521
- *
1522
- * Usage:
1523
- * POST /admin/clear-cache
1524
- * Headers: Authorization: Bearer <AGENTSHIELD_ADMIN_API_KEY>
1525
- * Body: { "agent_did": "did:key:z6Mk..." }
1526
- *
1527
- * Response:
1528
- * { "success": true, "message": "Cache cleared", "agent_did": "..." }
1529
- */
1530
- app.post("/admin/clear-cache", async (c) => {
1531
- const env = c.env as PrefixedCloudflareEnv;
1532
-
1533
- // Parse request body first to get agent_did
1534
- const body = await c.req.json().catch(() => ({}));
1535
- const agentDid = body.agent_did;
1536
-
1537
- if (!agentDid || typeof agentDid !== 'string') {
1538
- return c.json({
1539
- success: false,
1540
- error: "Bad Request - agent_did required in body"
1541
- }, 400);
1542
- }
1543
-
1544
- // Extract API key from Authorization header
1545
- const authHeader = c.req.header("Authorization");
1546
- if (!authHeader || !authHeader.startsWith("Bearer ")) {
1547
- return c.json({
1548
- success: false,
1549
- error: "Unauthorized - Missing or invalid Authorization header"
1550
- }, 401);
1551
- }
1552
-
1553
- const apiKey = authHeader.slice(7); // Remove "Bearer " prefix
1554
-
1555
- // Validate API key by making a test call to AgentShield
1556
- // Use the bouncer config endpoint as the validation mechanism
1557
- const agentShieldUrl = env.AGENTSHIELD_API_URL || "https://kya.vouched.id";
1558
- const validationUrl = \`\${agentShieldUrl}/api/v1/bouncer/config?agent_did=\${encodeURIComponent(agentDid)}\`;
1559
-
1560
- try {
1561
- const validationResponse = await fetch(validationUrl, {
1562
- method: 'GET',
1563
- headers: {
1564
- 'Content-Type': 'application/json',
1565
- 'Authorization': \`Bearer \${apiKey}\`
1566
- }
1567
- });
1568
-
1569
- if (!validationResponse.ok) {
1570
- console.warn('[Admin] API key validation failed:', validationResponse.status);
1571
- return c.json({
1572
- success: false,
1573
- error: "Unauthorized - Invalid API key"
1574
- }, 401);
1575
- }
1576
-
1577
- // API key is valid, proceed to clear cache
1578
- console.log('[Admin] API key validated successfully');
1579
- } catch (error) {
1580
- console.error('[Admin] API key validation error:', error);
1581
- return c.json({
1582
- success: false,
1583
- error: "Failed to validate API key with AgentShield"
1584
- }, 500);
1585
- }
1586
-
1587
- // Clear cache from KV
1588
- // Cache key format: KVToolProtectionCache uses 'tool-protection:' prefix + agentDid
1589
- // Since we're accessing KV directly (not through cache service), we need the full key
1590
- const cacheKey = \`tool-protection:\${agentDid}\`;
1591
- const kvNamespace = env.${className.toUpperCase()}_TOOL_PROTECTION_KV;
1592
-
1593
- if (!kvNamespace) {
1594
- return c.json({
1595
- success: false,
1596
- error: "Tool protection KV namespace not configured"
1597
- }, 500);
1598
- }
1599
-
1600
- try {
1601
- // Log before and after for debugging
1602
- const before = await kvNamespace.get(cacheKey);
1603
- await kvNamespace.delete(cacheKey);
1604
- const after = await kvNamespace.get(cacheKey);
1605
-
1606
- console.log('[Admin] Cache clear operation', {
1607
- agentDid: agentDid.slice(0, 20) + '...',
1608
- cacheKey,
1609
- hadValue: !!before,
1610
- cleared: !after,
1611
- });
1612
-
1613
- return c.json({
1614
- success: true,
1615
- message: "Cache cleared successfully. Next tool call will fetch fresh config from AgentShield.",
1616
- agent_did: agentDid,
1617
- cache_key: cacheKey,
1618
- had_value: !!before,
1619
- });
1620
- } catch (error) {
1621
- console.error('[Admin] Failed to clear cache:', error);
1622
- return c.json({
1623
- success: false,
1624
- error: "Internal error clearing cache",
1625
- details: error instanceof Error ? error.message : String(error)
1626
- }, 500);
1627
- }
1628
- });
1629
-
1630
- /**
1631
- * OAuth Authorization Code Flow callback handler
1632
- *
1633
- * Handles the redirect from AgentShield after user approves delegation.
1634
- * Exchanges authorization code for delegation token and stores in KV.
1635
- *
1636
- * This endpoint is called by AgentShield after user approves delegation:
1637
- * 1. Receives authorization code and state from query params
1638
- * 2. Exchanges code for delegation token with AgentShield API
1639
- * 3. Stores token in KV with session ID as key
1640
- * 4. Returns success page to user
1641
- */
1642
- app.get('/oauth/callback', async (c) => {
1643
- const env = c.env as PrefixedCloudflareEnv;
1644
- const handler = createOAuthCallbackHandler({
1645
- agentShieldApiUrl: env.AGENTSHIELD_API_URL || 'https://kya.vouched.id',
1646
- delegationStorage: env.${className.toUpperCase()}_DELEGATION_STORAGE,
1647
- autoClose: true,
1648
- autoCloseDelay: 5000
1649
- });
1650
- // Cast to HonoContext - Hono's context implements all required properties
1651
- return await handler(c as unknown as HonoContext);
1652
- });
1653
-
1654
- // Multi-instance DO routing using McpAgent's getInstanceId() override
1655
- // The ${pascalClassName}MCP class overrides getInstanceId() to enable session-based
1656
- // or shard-based routing while preserving PartyServer compatibility.
1657
- //
1658
- // Routing strategies (configured via DO_ROUTING_STRATEGY env var):
1659
- // - 'session' (default): One DO per MCP session - ensures data isolation
1660
- // - 'shard': Hash-based distribution across N shards - for high load
1661
- //
1662
- // McpAgent automatically routes to the correct DO instance using the ID
1663
- // returned by getInstanceId(), maintaining full routing context.
1664
- app.mount("/sse", ${pascalClassName}MCP.serveSSE("/sse").fetch, { replaceRequest: false });
1665
- app.mount("/mcp", ${pascalClassName}MCP.serve("/mcp").fetch, { replaceRequest: false });
1666
-
1667
- export default app;
1668
792
  `;
1669
793
  fs.writeFileSync(path.join(srcDir, "index.ts"), indexContent);
1670
- // Create wrangler.toml with optional API key
1671
794
  const wranglerContent = `#:schema node_modules/wrangler/config-schema.json
1672
795
  name = "${projectName}"
1673
796
  main = "src/index.ts"
@@ -1927,6 +1050,14 @@ dist/
1927
1050
  *.log
1928
1051
  `;
1929
1052
  fs.writeFileSync(path.join(projectPath, ".gitignore"), gitignoreContent);
1053
+ // Create .npmrc to suppress harmless deprecation warnings
1054
+ const npmrcContent = `# Suppress deprecation warnings from transitive dependencies
1055
+ # These warnings are harmless and don't affect functionality
1056
+ # They come from: inflight (glob@7), phin (jimp), rimraf (del), glob (rimraf), node-domexception (node-fetch)
1057
+ # Will be resolved when upstream packages update their dependencies
1058
+ loglevel=error
1059
+ `;
1060
+ fs.writeFileSync(path.join(projectPath, ".npmrc"), npmrcContent);
1930
1061
  // Create README.md
1931
1062
  const readmeContent = `# ${projectName}
1932
1063