├── tsconfig.json ├── .gitignore ├── package.json ├── LICENSE ├── README.md └── src └── index.ts /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "ES2020", 4 | "module": "ES2020", 5 | "moduleResolution": "node", 6 | "esModuleInterop": true, 7 | "strict": true, 8 | "outDir": "build", 9 | "rootDir": "src", 10 | "declaration": true, 11 | "skipLibCheck": true 12 | }, 13 | "include": ["src/**/*"], 14 | "exclude": ["node_modules", "build"] 15 | } -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Dependencies 2 | node_modules/ 3 | npm-debug.log* 4 | yarn-debug.log* 5 | yarn-error.log* 6 | 7 | # Build output 8 | build/ 9 | dist/ 10 | 11 | # IDE and editor files 12 | .idea/ 13 | .vscode/ 14 | *.swp 15 | *.swo 16 | .DS_Store 17 | 18 | # Environment variables 19 | .env 20 | .env.local 21 | .env.*.local 22 | 23 | # Logs 24 | logs/ 25 | *.log 26 | 27 | # Test coverage 28 | coverage/ -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@modelcontextprotocol/server-framer-plugin", 3 | "version": "0.1.0", 4 | "description": "MCP server for creating and managing Framer plugins with web3 capabilities", 5 | "type": "module", 6 | "main": "build/index.js", 7 | "scripts": { 8 | "build": "tsc && node -e \"require('fs').chmodSync('build/index.js', '755')\"", 9 | "dev": "tsc -w", 10 | "start": "node build/index.js", 11 | "test": "echo \"Error: no test specified\" && exit 1" 12 | }, 13 | "keywords": [ 14 | "mcp", 15 | "framer", 16 | "plugin", 17 | "web3" 18 | ], 19 | "author": "", 20 | "license": "MIT", 21 | "dependencies": { 22 | "@modelcontextprotocol/sdk": "latest", 23 | "fs-extra": "^11.1.1" 24 | }, 25 | "devDependencies": { 26 | "@types/fs-extra": "^11.0.1", 27 | "@types/node": "^20.4.2", 28 | "typescript": "^5.1.6" 29 | } 30 | } -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2024 Sheshiyer 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Framer Plugin MCP Server 2 | 3 | A Model Context Protocol (MCP) server that enables creation and management of Framer plugins with web3 capabilities. This server provides tools for creating, building, and managing Framer plugins with integrated web3 features like wallet connections, contract interactions, and NFT displays. 4 | 5 | ## Features 6 | 7 | - Create new Framer plugins with web3 capabilities 8 | - Build plugins for production 9 | - Integrated web3 features: 10 | - Wallet Connect integration 11 | - Smart contract interactions 12 | - NFT display components 13 | 14 | ## Requirements 15 | 16 | - Node.js 16 or higher 17 | - NPM or Yarn 18 | - Framer desktop app for testing plugins 19 | 20 | ## Installation 21 | 22 | 1. Clone this repository: 23 | ```bash 24 | git clone https://github.com/sheshiyer/framer-plugin-mcp.git 25 | cd framer-plugin-mcp 26 | ``` 27 | 28 | 2. Install dependencies: 29 | ```bash 30 | npm install 31 | ``` 32 | 33 | 3. Build the server: 34 | ```bash 35 | npm run build 36 | ``` 37 | 38 | ## Configuration 39 | 40 | Add the server to your MCP settings file: 41 | 42 | For Claude Desktop App (`~/Library/Application Support/Claude/claude_desktop_config.json`): 43 | ```json 44 | { 45 | "mcpServers": { 46 | "framer-plugin": { 47 | "command": "node", 48 | "args": ["/path/to/framer-plugin-mcp/build/index.js"] 49 | } 50 | } 51 | } 52 | ``` 53 | 54 | For Cursor/Claude Dev (`~/Library/Application Support/Cursor/User/globalStorage/saoudrizwan.claude-dev/settings/cline_mcp_settings.json`): 55 | ```json 56 | { 57 | "mcpServers": { 58 | "framer-plugin": { 59 | "command": "node", 60 | "args": ["/path/to/framer-plugin-mcp/build/index.js"] 61 | } 62 | } 63 | } 64 | ``` 65 | 66 | ## Usage 67 | 68 | Once configured, the server provides the following tools: 69 | 70 | ### create_plugin 71 | Creates a new Framer plugin project with web3 capabilities. 72 | 73 | Parameters: 74 | - name: Plugin name 75 | - description: Plugin description 76 | - outputPath: Output directory path 77 | - web3Features: Array of features to include (wallet-connect, contract-interaction, nft-display) 78 | 79 | Example: 80 | ```json 81 | { 82 | "name": "my-web3-plugin", 83 | "description": "A Framer plugin with web3 features", 84 | "outputPath": "./plugins/my-web3-plugin", 85 | "web3Features": ["wallet-connect", "nft-display"] 86 | } 87 | ``` 88 | 89 | ### build_plugin 90 | Builds a Framer plugin project for production. 91 | 92 | Parameters: 93 | - pluginPath: Path to plugin directory 94 | 95 | Example: 96 | ```json 97 | { 98 | "pluginPath": "./plugins/my-web3-plugin" 99 | } 100 | ``` 101 | 102 | ## Contributing 103 | 104 | Contributions are welcome! Please feel free to submit a Pull Request. 105 | 106 | ## License 107 | 108 | MIT License - see LICENSE file for details -------------------------------------------------------------------------------- /src/index.ts: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | import { Server } from '@modelcontextprotocol/sdk/server/index.js'; 3 | import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js'; 4 | import { 5 | CallToolRequestSchema, 6 | ErrorCode, 7 | ListToolsRequestSchema, 8 | McpError, 9 | } from '@modelcontextprotocol/sdk/types.js'; 10 | import fs from 'fs-extra'; 11 | import path from 'path'; 12 | 13 | interface CreatePluginArgs { 14 | name: string; 15 | description: string; 16 | outputPath: string; 17 | web3Features?: string[]; 18 | } 19 | 20 | interface BuildPluginArgs { 21 | pluginPath: string; 22 | } 23 | 24 | interface PackageJson { 25 | name: string; 26 | version: string; 27 | description: string; 28 | main: string; 29 | scripts: Record; 30 | dependencies: Record; 31 | devDependencies: Record; 32 | } 33 | 34 | class FramerPluginServer { 35 | private server: Server; 36 | 37 | constructor() { 38 | this.server = new Server( 39 | { 40 | name: 'framer-plugin', 41 | version: '0.1.0', 42 | }, 43 | { 44 | capabilities: { 45 | tools: {}, 46 | }, 47 | } 48 | ); 49 | 50 | this.setupToolHandlers(); 51 | 52 | // Error handling 53 | this.server.onerror = (error: Error) => console.error('[MCP Error]', error); 54 | process.on('SIGINT', async () => { 55 | await this.server.close(); 56 | process.exit(0); 57 | }); 58 | } 59 | 60 | private setupToolHandlers() { 61 | this.server.setRequestHandler(ListToolsRequestSchema, async () => ({ 62 | tools: [ 63 | { 64 | name: 'create_plugin', 65 | description: 'Create a new Framer plugin project with web3 capabilities', 66 | inputSchema: { 67 | type: 'object', 68 | properties: { 69 | name: { 70 | type: 'string', 71 | description: 'Plugin name' 72 | }, 73 | description: { 74 | type: 'string', 75 | description: 'Plugin description' 76 | }, 77 | outputPath: { 78 | type: 'string', 79 | description: 'Output directory path' 80 | }, 81 | web3Features: { 82 | type: 'array', 83 | items: { 84 | type: 'string', 85 | enum: ['wallet-connect', 'contract-interaction', 'nft-display'] 86 | }, 87 | description: 'Web3 features to include' 88 | } 89 | }, 90 | required: ['name', 'description', 'outputPath'] 91 | } 92 | }, 93 | { 94 | name: 'build_plugin', 95 | description: 'Build a Framer plugin project', 96 | inputSchema: { 97 | type: 'object', 98 | properties: { 99 | pluginPath: { 100 | type: 'string', 101 | description: 'Path to plugin directory' 102 | } 103 | }, 104 | required: ['pluginPath'] 105 | } 106 | } 107 | ] 108 | })); 109 | 110 | this.server.setRequestHandler(CallToolRequestSchema, async (request) => { 111 | switch (request.params.name) { 112 | case 'create_plugin': 113 | return this.handleCreatePlugin(request.params.arguments as unknown as CreatePluginArgs); 114 | case 'build_plugin': 115 | return this.handleBuildPlugin(request.params.arguments as unknown as BuildPluginArgs); 116 | default: 117 | throw new McpError( 118 | ErrorCode.MethodNotFound, 119 | `Unknown tool: ${request.params.name}` 120 | ); 121 | } 122 | }); 123 | } 124 | 125 | private async handleCreatePlugin(args: CreatePluginArgs) { 126 | try { 127 | const pluginDir = path.resolve(args.outputPath); 128 | await fs.ensureDir(pluginDir); 129 | 130 | // Create package.json 131 | const packageJson: PackageJson = { 132 | name: args.name, 133 | version: '1.0.0', 134 | description: args.description, 135 | main: 'dist/index.js', 136 | scripts: { 137 | build: 'vite build', 138 | dev: 'vite' 139 | }, 140 | dependencies: { 141 | '@framer/framer.motion': '^10.0.0', 142 | 'react': '^18.2.0', 143 | 'react-dom': '^18.2.0' 144 | }, 145 | devDependencies: { 146 | '@types/react': '^18.2.0', 147 | 'typescript': '^5.0.0', 148 | 'vite': '^4.0.0', 149 | '@vitejs/plugin-react': '^4.0.0' 150 | } 151 | }; 152 | 153 | // Add web3 dependencies if features are requested 154 | if (args.web3Features?.includes('wallet-connect')) { 155 | packageJson.dependencies = { 156 | ...packageJson.dependencies, 157 | '@web3-react/core': '^8.2.0', 158 | '@web3-react/injected-connector': '^6.0.7', 159 | 'ethers': '^6.7.0' 160 | }; 161 | } 162 | 163 | await fs.writeJSON(path.join(pluginDir, 'package.json'), packageJson, { spaces: 2 }); 164 | 165 | // Create tsconfig.json 166 | const tsConfig = { 167 | compilerOptions: { 168 | target: 'ES2020', 169 | lib: ['DOM', 'DOM.Iterable', 'ESNext'], 170 | module: 'ESNext', 171 | skipLibCheck: true, 172 | moduleResolution: 'bundler', 173 | allowImportingTsExtensions: true, 174 | resolveJsonModule: true, 175 | isolatedModules: true, 176 | noEmit: true, 177 | jsx: 'react-jsx', 178 | strict: true, 179 | noUnusedLocals: true, 180 | noUnusedParameters: true, 181 | noFallthroughCasesInSwitch: true 182 | }, 183 | include: ['src'], 184 | references: [{ path: './tsconfig.node.json' }] 185 | }; 186 | 187 | await fs.writeJSON(path.join(pluginDir, 'tsconfig.json'), tsConfig, { spaces: 2 }); 188 | 189 | // Create tsconfig.node.json 190 | const tsNodeConfig = { 191 | compilerOptions: { 192 | composite: true, 193 | skipLibCheck: true, 194 | module: 'ESNext', 195 | moduleResolution: 'bundler', 196 | allowSyntheticDefaultImports: true 197 | }, 198 | include: ['vite.config.ts'] 199 | }; 200 | 201 | await fs.writeJSON(path.join(pluginDir, 'tsconfig.node.json'), tsNodeConfig, { spaces: 2 }); 202 | 203 | // Create vite.config.ts 204 | const viteConfig = ` 205 | import { defineConfig } from 'vite'; 206 | import react from '@vitejs/plugin-react'; 207 | 208 | export default defineConfig({ 209 | plugins: [react()], 210 | build: { 211 | lib: { 212 | entry: 'src/index.tsx', 213 | name: '${args.name}', 214 | formats: ['es'], 215 | fileName: 'index' 216 | }, 217 | rollupOptions: { 218 | external: ['react', 'react-dom'], 219 | output: { 220 | globals: { 221 | react: 'React', 222 | 'react-dom': 'ReactDOM' 223 | } 224 | } 225 | } 226 | } 227 | }); 228 | `; 229 | 230 | await fs.writeFile(path.join(pluginDir, 'vite.config.ts'), viteConfig); 231 | 232 | // Create src directory and base files 233 | const srcDir = path.join(pluginDir, 'src'); 234 | await fs.ensureDir(srcDir); 235 | 236 | // Create index.tsx 237 | let indexContent = ` 238 | import { addPropertyControls, ControlType } from 'framer'; 239 | import { motion } from 'framer-motion'; 240 | import React from 'react'; 241 | 242 | export default function ${args.name.replace(/-/g, '_')}() { 243 | return ( 244 | 254 | ); 255 | } 256 | 257 | addPropertyControls(${args.name.replace(/-/g, '_')}, { 258 | text: { 259 | type: ControlType.String, 260 | title: 'Text', 261 | defaultValue: 'Hello World', 262 | }, 263 | }); 264 | `; 265 | 266 | if (args.web3Features?.includes('wallet-connect')) { 267 | indexContent = ` 268 | import { addPropertyControls, ControlType } from 'framer'; 269 | import { motion } from 'framer-motion'; 270 | import React from 'react'; 271 | import { Web3ReactProvider, useWeb3React } from '@web3-react/core'; 272 | import { InjectedConnector } from '@web3-react/injected-connector'; 273 | import { ethers } from 'ethers'; 274 | 275 | const injected = new InjectedConnector({ 276 | supportedChainIds: [1, 3, 4, 5, 42], 277 | }); 278 | 279 | function Web3Button() { 280 | const { activate, active, account, library } = useWeb3React(); 281 | 282 | const connect = async () => { 283 | try { 284 | await activate(injected); 285 | } catch (error) { 286 | console.error('Error connecting:', error); 287 | } 288 | }; 289 | 290 | return ( 291 | 304 | {active ? \`Connected: \${account?.slice(0, 6)}...\${account?.slice(-4)}\` : 'Connect Wallet'} 305 | 306 | ); 307 | } 308 | 309 | export default function ${args.name.replace(/-/g, '_')}() { 310 | return ( 311 | new ethers.BrowserProvider(provider)}> 312 | 313 | 314 | ); 315 | } 316 | 317 | addPropertyControls(${args.name.replace(/-/g, '_')}, { 318 | text: { 319 | type: ControlType.String, 320 | title: 'Text', 321 | defaultValue: 'Connect Wallet', 322 | }, 323 | }); 324 | `; 325 | } 326 | 327 | await fs.writeFile(path.join(srcDir, 'index.tsx'), indexContent); 328 | 329 | return { 330 | content: [ 331 | { 332 | type: 'text', 333 | text: `Successfully created Framer plugin project at ${pluginDir}` 334 | } 335 | ] 336 | }; 337 | } catch (error) { 338 | throw new McpError( 339 | ErrorCode.InternalError, 340 | `Failed to create plugin: ${error instanceof Error ? error.message : String(error)}` 341 | ); 342 | } 343 | } 344 | 345 | private async handleBuildPlugin(args: BuildPluginArgs) { 346 | try { 347 | const pluginDir = path.resolve(args.pluginPath); 348 | 349 | // Verify plugin directory exists 350 | if (!await fs.pathExists(pluginDir)) { 351 | throw new Error(`Plugin directory not found: ${pluginDir}`); 352 | } 353 | 354 | // Run build command 355 | const { execSync } = require('child_process'); 356 | execSync('npm run build', { cwd: pluginDir, stdio: 'inherit' }); 357 | 358 | return { 359 | content: [ 360 | { 361 | type: 'text', 362 | text: `Successfully built plugin at ${pluginDir}` 363 | } 364 | ] 365 | }; 366 | } catch (error) { 367 | throw new McpError( 368 | ErrorCode.InternalError, 369 | `Failed to build plugin: ${error instanceof Error ? error.message : String(error)}` 370 | ); 371 | } 372 | } 373 | 374 | async run() { 375 | const transport = new StdioServerTransport(); 376 | await this.server.connect(transport); 377 | console.error('Framer Plugin MCP server running on stdio'); 378 | } 379 | } 380 | 381 | const server = new FramerPluginServer(); 382 | server.run().catch(console.error); --------------------------------------------------------------------------------