UNPKG

octocode-mcp

Version:

Model Context Protocol (MCP) server for advanced GitHub repository analysis, code discovery, and npm package exploration. Provides AI assistants with powerful tools to search, analyze, and understand codebases across GitHub and npm ecosystems.

3 lines (2 loc) 6.27 kB
#!/usr/bin/env node import{C as e}from"./index-DnOJNPC2.js";import{OAuthManager as t}from"./oauthManager-BvuhrXGx.js";import{GitHubAppManager as o}from"./githubAppManager-DIllRVGO.js";import"node:process";import"events";import"crypto";import"child_process";import"http";import"https";import"url";import"path";import"fs";import"util";import"stream";import"assert";import"tty";import"os";import"zlib";class r{static instance;oauthManager=null;githubAppManager=null;static getInstance(){return this.instance||(this.instance=new r),this.instance}async initialize(){const r=e.getConfig();r.oauth?.enabled&&(this.oauthManager=t.getInstance(),this.oauthManager.initialize()),r.githubApp?.enabled&&(this.githubAppManager=o.getInstance(),this.githubAppManager.initialize())}createAuthChallenge(t,o,r,a){const i=`${e.getConfig().githubHost||"https://github.com"}/.well-known/mcp-resource-metadata`,n={scheme:"Bearer",...t&&{realm:t},...o&&{scope:o},...r&&{error:r},...a&&{error_description:a},resource_metadata:i};return{status:401,headers:{"WWW-Authenticate":this.formatWWWAuthenticateHeader(n),"Content-Type":"application/json"},body:{error:r||"unauthorized",error_description:a||"Valid GitHub access token required in Authorization header",resource_metadata:i}}}getProtectedResourceMetadata(){const t=e.getConfig(),o=t.githubHost||"https://github.com",r=t.githubHost?`${t.githubHost}/api/v3`:"https://api.github.com",a=[];return t.oauth?.enabled&&a.push({issuer:o,authorization_endpoint:t.oauth.authorizationUrl||`${o}/login/oauth/authorize`,token_endpoint:t.oauth.tokenUrl||`${o}/login/oauth/access_token`,scopes_supported:t.oauth.scopes||["repo","read:user","read:org"],response_types_supported:["code"],grant_types_supported:["authorization_code","refresh_token"],code_challenge_methods_supported:["S256"],token_endpoint_auth_methods_supported:["client_secret_post"],revocation_endpoint:`${o}/login/oauth/revoke`}),t.githubApp?.enabled&&a.push({issuer:o,authorization_endpoint:`${o}/login/oauth/authorize`,token_endpoint:`${r}/app/installations/${t.githubApp.installationId}/access_tokens`,scopes_supported:["repo","read:user","read:org"],response_types_supported:["code"],grant_types_supported:["authorization_code"],code_challenge_methods_supported:["S256"],token_endpoint_auth_methods_supported:["private_key_jwt"]}),{authorization_servers:a,resource_server:{resource_server_id:"github-api",resource_server_name:"GitHub API",resource_server_icon:"https://github.com/favicon.ico",resource_server_description:"GitHub API access for repository and organization management"},scopes_supported:["repo","repo:status","repo_deployment","public_repo","repo:invite","security_events","read:user","user:email","user:follow","read:org","write:org","admin:org","read:public_key","write:public_key","admin:public_key","read:repo_hook","write:repo_hook","admin:repo_hook","read:org_hook","write:org_hook","admin:org_hook","gist","notifications","read:discussion","write:discussion"],client_registration:{supported:!1}}}async validateBearerToken(t){try{const o=t.match(/^Bearer\s+(.+)$/i);if(!o)return{valid:!1,error:"Invalid authorization header format. Expected: Bearer <token>"};const r=o[1],a=e.getConfig(),i=a.githubHost?`${a.githubHost}/api/v3`:"https://api.github.com",n=await fetch(`${i}/user`,{headers:{Authorization:`Bearer ${r}`,"User-Agent":`octocode-mcp/${a.version}`,Accept:"application/vnd.github.v3+json"}});return n.ok?{valid:!0,token:r,scopes:n.headers.get("x-oauth-scopes")?.split(", ")||[]}:{valid:!1,error:`Token validation failed: ${n.status} ${n.statusText}`}}catch(e){return{valid:!1,error:e instanceof Error?e.message:String(e)}}}async handleAuthenticatedRequest(e){const t=e.headers.Authorization||e.headers.authorization;if(!t)return this.createAuthChallenge("github-api","repo read:user","missing_token","Authorization header with Bearer token is required");const o=await this.validateBearerToken(t);return o.valid?{status:200,headers:{"Content-Type":"application/json"},body:{authenticated:!0,token:o.token,scopes:o.scopes}}:this.createAuthChallenge("github-api","repo read:user","invalid_token",o.error||"Token validation failed")}async getAuthorizationUrl(t,o,r,a){if(!this.oauthManager)throw new Error("OAuth not configured");const i=e.getConfig(),n=t||i.oauth?.clientId,s=o||i.oauth?.redirectUri,p=r||i.oauth?.scopes||["repo","read:user"];if(!n||!s)throw new Error("OAuth client ID and redirect URI required");const{codeVerifier:u,codeChallenge:h}=this.oauthManager.generatePKCEParams(),c=a||this.oauthManager.generateState();return{authorizationUrl:this.oauthManager.createAuthorizationUrl(c,h,{client_id:n,redirect_uri:s,scope:p.join(" ")}),state:c,codeVerifier:u}}async exchangeCodeForToken(e,t,o){if(!this.oauthManager)throw new Error("OAuth not configured");const r=await this.oauthManager.exchangeCodeForToken(e,t,o);return{accessToken:r.accessToken,refreshToken:r.refreshToken,tokenType:r.tokenType,expiresIn:r.expiresIn,scope:r.scope}}async getGitHubAppToken(e){if(!this.githubAppManager)throw new Error("GitHub App not configured");const t=await this.githubAppManager.getInstallationToken(e);return{token:t.token,expiresAt:t.expiresAt,permissions:t.permissions}}getAuthorizationServerMetadata(){const t=e.getConfig();if(!t.oauth?.enabled)return null;const o=t.githubHost||"https://github.com";return{issuer:o,authorization_endpoint:t.oauth.authorizationUrl||`${o}/login/oauth/authorize`,token_endpoint:t.oauth.tokenUrl||`${o}/login/oauth/access_token`,scopes_supported:t.oauth.scopes||["repo","read:user","read:org"],response_types_supported:["code"],grant_types_supported:["authorization_code","refresh_token"],code_challenge_methods_supported:["S256"],token_endpoint_auth_methods_supported:["client_secret_post"],revocation_endpoint:`${o}/login/oauth/revoke`}}formatWWWAuthenticateHeader(e){const t=[`${e.scheme}`];return e.realm&&t.push(`realm="${e.realm}"`),e.scope&&t.push(`scope="${e.scope}"`),e.error&&t.push(`error="${e.error}"`),e.error_description&&t.push(`error_description="${e.error_description}"`),e.resource_metadata&&t.push(`resource_metadata="${e.resource_metadata}"`),t.join(", ")}}async function a(){const e=r.getInstance();return await e.initialize(),e}export{r as MCPAuthProtocol,a as createMCPAuthProtocol,r as default};