a fancy canvas mcp server!
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

feat: update metadata

+37 -29
+12
src/lib/mcp-server.ts
··· 10 10 11 11 // Create MCP Server instance with user context 12 12 export function createMcpServer(userId: number): Server { 13 + const BASE_URL = process.env.BASE_URL || "http://localhost:3000"; 14 + 13 15 const server = new Server( 14 16 { 15 17 name: "canvas-mcp", 16 18 version: "1.0.0", 19 + title: "Canvas LMS", 20 + description: "Access your Canvas courses, assignments, grades, and announcements", 21 + websiteUrl: BASE_URL, 22 + icons: [ 23 + { 24 + src: `${BASE_URL}/favicon.ico`, 25 + mimeType: "image/x-icon", 26 + sizes: ["32x32"], 27 + }, 28 + ], 17 29 }, 18 30 { 19 31 capabilities: {
+15 -16
src/public/dashboard.html
··· 1 1 <!DOCTYPE html> 2 2 <html lang="en"> 3 + 3 4 <head> 4 5 <meta charset="UTF-8"> 5 6 <meta name="viewport" content="width=device-width, initial-scale=1.0"> 6 7 <title>Dashboard - Canvas MCP</title> 8 + <link rel="icon" type="image/x-icon" href="./favicon.ico"> 7 9 <style> 8 10 * { 9 11 margin: 0; ··· 199 201 } 200 202 </style> 201 203 </head> 204 + 202 205 <body> 203 206 <header> 204 207 <h1>Dashboard</h1> ··· 213 216 <form id="connectCanvasForm"> 214 217 <div style="margin-bottom: 1rem;"> 215 218 <label style="display: block; margin-bottom: 0.5rem; font-weight: 500;">Canvas Domain</label> 216 - <input 217 - type="text" 218 - id="canvasDomain" 219 - placeholder="canvas.university.edu" 219 + <input type="text" id="canvasDomain" placeholder="canvas.university.edu" 220 220 style="width: 100%; padding: 0.75rem; border: 1px solid #ddd; border-radius: 4px; font-size: 1rem;" 221 - required 222 - /> 221 + required /> 223 222 </div> 224 223 <div style="margin-bottom: 1rem;"> 225 224 <label style="display: block; margin-bottom: 0.5rem; font-weight: 500;">Personal Access Token</label> 226 - <input 227 - type="password" 228 - id="accessToken" 229 - placeholder="Get this from Canvas → Settings → New Access Token" 225 + <input type="password" id="accessToken" placeholder="Get this from Canvas → Settings → New Access Token" 230 226 style="width: 100%; padding: 0.75rem; border: 1px solid #ddd; border-radius: 4px; font-size: 1rem;" 231 - required 232 - /> 227 + required /> 233 228 </div> 234 229 <button type="submit" style="width: 100%; padding: 0.75rem;">Connect Canvas</button> 235 - <div id="connectError" style="display: none; margin-top: 1rem; padding: 0.75rem; background: #fee; border: 1px solid #fcc; border-radius: 4px; color: #c33;"></div> 230 + <div id="connectError" 231 + style="display: none; margin-top: 1rem; padding: 0.75rem; background: #fee; border: 1px solid #fcc; border-radius: 4px; color: #c33;"> 232 + </div> 236 233 </form> 237 234 <details style="margin-top: 1.5rem;"> 238 235 <summary style="cursor: pointer; color: #666;">How to get a Personal Access Token</summary> ··· 285 282 </div> 286 283 287 284 <div id="apiKeyDisplay" style="display: none;"> 288 - <div style="background: #fff3cd; border: 1px solid #ffc107; padding: 1rem; border-radius: 4px; margin-bottom: 1rem;"> 285 + <div 286 + style="background: #fff3cd; border: 1px solid #ffc107; padding: 1rem; border-radius: 4px; margin-bottom: 1rem;"> 289 287 <strong>⚠️ Save this token now!</strong> You won't be able to see it again after leaving this page. 290 288 </div> 291 289 <div class="api-key-display"> ··· 567 565 try { 568 566 const response = await fetch('/api/auth/token-login', { 569 567 method: 'POST', 570 - headers: { 'Content-Type': 'application/json' }, 568 + headers: {'Content-Type': 'application/json'}, 571 569 credentials: 'include', 572 570 body: JSON.stringify({ 573 571 canvas_domain: domain, ··· 594 592 loadDashboard(); 595 593 </script> 596 594 </body> 597 - </html> 595 + 596 + </html>
+10 -13
src/public/index.html
··· 1 1 <!DOCTYPE html> 2 2 <html lang="en"> 3 + 3 4 <head> 4 5 <meta charset="UTF-8"> 5 6 <meta name="viewport" content="width=device-width, initial-scale=1.0"> 6 7 <title>Canvas MCP Server</title> 8 + <link rel="icon" type="image/x-icon" href="./favicon.ico"> 7 9 <style> 8 10 * { 9 11 margin: 0; ··· 144 146 } 145 147 </style> 146 148 </head> 149 + 147 150 <body> 148 151 <header> 149 152 <h1>Canvas MCP Server</h1> ··· 164 167 <h2>Get Started</h2> 165 168 <form id="loginForm"> 166 169 <label for="email">Email Address</label> 167 - <input 168 - type="email" 169 - id="email" 170 - name="email" 171 - placeholder="your.email@school.edu" 172 - autocomplete="email" 173 - required 174 - /> 170 + <input type="email" id="email" name="email" placeholder="your.email@school.edu" autocomplete="email" required /> 175 171 176 172 <button type="submit">Send Sign-In Link</button> 177 173 <div id="error" class="error"></div> ··· 189 185 190 186 <script type="module"> 191 187 // Check if already logged in 192 - fetch('/api/user/me', { credentials: 'include' }) 188 + fetch('/api/user/me', {credentials: 'include'}) 193 189 .then(r => { 194 190 if (r.ok) { 195 191 window.location.href = '/dashboard'; 196 192 } 197 193 }) 198 - .catch(() => {}); 194 + .catch(() => { }); 199 195 200 196 const form = document.getElementById('loginForm'); 201 197 const errorDiv = document.getElementById('error'); ··· 241 237 try { 242 238 const response = await fetch('/api/auth/request-magic-link', { 243 239 method: 'POST', 244 - headers: { 'Content-Type': 'application/json' }, 245 - body: JSON.stringify({ email }) 240 + headers: {'Content-Type': 'application/json'}, 241 + body: JSON.stringify({email}) 246 242 }); 247 243 248 244 const data = await response.json(); ··· 262 258 }); 263 259 </script> 264 260 </body> 265 - </html> 261 + 262 + </html>