@onyxsecurity/mcp-gateway 1.0.42 → 1.0.43
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.
- package/dist/bin/mcp-gateway.js +1 -1
- package/dist/bin/mcp-gateway.js.map +1 -0
- package/dist/chunk-CrDKVgUD.js +1 -0
- package/dist/helpers-DH8ZNg5N.js +92 -0
- package/dist/helpers-DH8ZNg5N.js.map +1 -0
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -0
- package/dist/main-xAo2ko_0.js +141 -0
- package/dist/main-xAo2ko_0.js.map +1 -0
- package/dist/normalizeUrl-CLNW7rYo.js +29 -0
- package/dist/normalizeUrl-CLNW7rYo.js.map +1 -0
- package/dist/pkce-ANRIC6ce.js.map +1 -0
- package/package.json +5 -4
- package/dist/chunk-wLQ3Su6Z.js +0 -1
- package/dist/helpers-D0p5Eli7.js +0 -92
- package/dist/main-DVYQSKXr.js +0 -141
- package/dist/normalizeUrl-KgtmxrqN.js +0 -77
package/dist/index.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import{Client as e,InMemoryEventStore as t,SSEClientTransport as n,Server as r,StdioServerTransport as i,StreamableHTTPClientTransport as a,config as o,normalizeUrl as s,proxyServer as c,startHTTPServer as l}from"./normalizeUrl-
|
|
1
|
+
import{Client as e,InMemoryEventStore as t,SSEClientTransport as n,Server as r,StdioServerTransport as i,StreamableHTTPClientTransport as a,config as o,normalizeUrl as s,proxyServer as c,startHTTPServer as l}from"./normalizeUrl-CLNW7rYo.js";let u=function(e){return e.HTTPStream=`HTTPStream`,e.SSE=`SSE`,e}({});const d=async({initStdioServer:t,initStreamClient:l,serverType:d,transportOptions:f={},url:p})=>{let m=s(p),h;switch(d){case u.SSE:h=new n(new URL(m),f);break;default:h=new a(new URL(m),f)}let g=l?await l():new e({name:`mcp-gateway`,version:o.appVersion},{capabilities:{}});await g.connect(h);let _=g.getServerVersion(),v=g.getServerCapabilities(),y=t?await t():new r(_,{capabilities:v}),b=new i;return await y.connect(b),await c({authorizer:void 0,client:g,server:y,serverCapabilities:v}),y},f=(e,t)=>{let n=e.close.bind(e),r=e.onclose?.bind(e),i=e.onerror?.bind(e),a=e.onmessage?.bind(e),o=e.send.bind(e),s=e.start.bind(e);return e.close=async()=>(t({type:`close`}),n?.()),e.onclose=async()=>(t({type:`onclose`}),r?.()),e.onerror=async e=>(t({error:e,type:`onerror`}),i?.(e)),e.onmessage=async e=>(t({message:e,type:`onmessage`}),a?.(e)),e.send=async e=>(t({message:e,type:`send`}),o?.(e)),e.start=async()=>(t({type:`start`}),s?.()),e};export{t as InMemoryEventStore,u as ServerType,c as proxyServer,l as startHTTPServer,d as startStdioServer,f as tapTransport};
|
|
2
2
|
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","names":["transport: SSEClientTransport | StreamableHTTPClientTransport"],"sources":["../src/startStdioServer.ts","../src/tapTransport.ts"],"sourcesContent":["import { Client } from \"@modelcontextprotocol/sdk/client/index.js\";\nimport {\n\tSSEClientTransport,\n\ttype SSEClientTransportOptions,\n} from \"@modelcontextprotocol/sdk/client/sse.js\";\nimport {\n\tStreamableHTTPClientTransport,\n\ttype StreamableHTTPClientTransportOptions,\n} from \"@modelcontextprotocol/sdk/client/streamableHttp.js\";\nimport { Server } from \"@modelcontextprotocol/sdk/server/index.js\";\nimport { StdioServerTransport } from \"@modelcontextprotocol/sdk/server/stdio.js\";\n\nimport { config } from \"./config/env.js\";\nimport { proxyServer } from \"./proxyServer.js\";\nimport { normalizeUrl } from \"./utils/normalizeUrl.js\";\n\nexport enum ServerType {\n\tHTTPStream = \"HTTPStream\",\n\tSSE = \"SSE\",\n}\n\nexport const startStdioServer = async ({\n\tinitStdioServer,\n\tinitStreamClient,\n\tserverType,\n\ttransportOptions = {},\n\turl,\n}: {\n\tinitStdioServer?: () => Promise<Server>;\n\tinitStreamClient?: () => Promise<Client>;\n\tserverType: ServerType;\n\ttransportOptions?:\n\t\t| SSEClientTransportOptions\n\t\t| StreamableHTTPClientTransportOptions;\n\turl: string;\n}): Promise<Server> => {\n\t// Normalize URL to avoid redirect issues on Windows (POST to GET conversion)\n\tconst normalizedUrl = normalizeUrl(url);\n\tlet transport: SSEClientTransport | StreamableHTTPClientTransport;\n\tswitch (serverType) {\n\t\tcase ServerType.SSE:\n\t\t\ttransport = new SSEClientTransport(new URL(normalizedUrl), transportOptions);\n\t\t\tbreak;\n\t\tdefault:\n\t\t\ttransport = new StreamableHTTPClientTransport(\n\t\t\t\tnew URL(normalizedUrl),\n\t\t\t\ttransportOptions,\n\t\t\t);\n\t}\n\tconst streamClient = initStreamClient\n\t\t? await initStreamClient()\n\t\t: new Client(\n\t\t\t\t{\n\t\t\t\t\tname: \"mcp-gateway\",\n\t\t\t\t\tversion: config.appVersion,\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\tcapabilities: {},\n\t\t\t\t},\n\t\t\t);\n\n\tawait streamClient.connect(transport);\n\n\tconst serverVersion = streamClient.getServerVersion() as {\n\t\tname: string;\n\t\tversion: string;\n\t};\n\n\tconst serverCapabilities = streamClient.getServerCapabilities() as {\n\t\tcapabilities: Record<string, unknown>;\n\t};\n\n\tconst stdioServer = initStdioServer\n\t\t? await initStdioServer()\n\t\t: new Server(serverVersion, {\n\t\t\t\tcapabilities: serverCapabilities,\n\t\t\t});\n\n\tconst stdioTransport = new StdioServerTransport();\n\n\tawait stdioServer.connect(stdioTransport);\n\n\tawait proxyServer({\n\t\tauthorizer: undefined,\n\t\tclient: streamClient,\n\t\tserver: stdioServer,\n\t\tserverCapabilities,\n\t});\n\n\treturn stdioServer;\n};\n","import { Transport } from \"@modelcontextprotocol/sdk/shared/transport.js\";\nimport { JSONRPCMessage } from \"@modelcontextprotocol/sdk/types.js\";\n\ntype TransportEvent =\n | {\n error: Error;\n type: \"onerror\";\n }\n | {\n message: JSONRPCMessage;\n type: \"onmessage\";\n }\n | {\n message: JSONRPCMessage;\n type: \"send\";\n }\n | {\n type: \"close\";\n }\n | {\n type: \"onclose\";\n }\n | {\n type: \"start\";\n };\n\nexport const tapTransport = (\n transport: Transport,\n eventHandler: (event: TransportEvent) => void,\n): Transport => {\n const originalClose = transport.close.bind(transport);\n const originalOnClose = transport.onclose?.bind(transport);\n const originalOnError = transport.onerror?.bind(transport);\n const originalOnMessage = transport.onmessage?.bind(transport);\n const originalSend = transport.send.bind(transport);\n const originalStart = transport.start.bind(transport);\n\n transport.close = async () => {\n eventHandler({\n type: \"close\",\n });\n\n return originalClose?.();\n };\n\n transport.onclose = async () => {\n eventHandler({\n type: \"onclose\",\n });\n\n return originalOnClose?.();\n };\n\n transport.onerror = async (error: Error) => {\n eventHandler({\n error,\n type: \"onerror\",\n });\n\n return originalOnError?.(error);\n };\n\n transport.onmessage = async (message: JSONRPCMessage) => {\n eventHandler({\n message,\n type: \"onmessage\",\n });\n\n return originalOnMessage?.(message);\n };\n\n transport.send = async (message: JSONRPCMessage) => {\n eventHandler({\n message,\n type: \"send\",\n });\n\n return originalSend?.(message);\n };\n\n transport.start = async () => {\n eventHandler({\n type: \"start\",\n });\n\n return originalStart?.();\n };\n\n return transport;\n};\n"],"mappings":"iPAgBA,IAAY,EAAA,SAAA,EAAL,OACN,GAAA,WAAA,aACA,EAAA,IAAA,aAGD,MAAa,EAAmB,MAAO,CACtC,kBACA,mBACA,aACA,mBAAmB,EAAE,CACrB,SASsB,CAEtB,IAAM,EAAgB,EAAa,EAAI,CACnCA,EACJ,OAAQ,EAAR,CACC,KAAK,EAAW,IACf,EAAY,IAAI,EAAmB,IAAI,IAAI,EAAc,CAAE,EAAiB,CAC5E,MACD,QACC,EAAY,IAAI,EACf,IAAI,IAAI,EAAc,CACtB,EACA,CAEH,IAAM,EAAe,EAClB,MAAM,GAAkB,CACxB,IAAI,EACJ,CACC,KAAM,cACN,QAAS,EAAO,WAChB,CACD,CACC,aAAc,EAAE,CAChB,CACD,CAEH,MAAM,EAAa,QAAQ,EAAU,CAErC,IAAM,EAAgB,EAAa,kBAAkB,CAK/C,EAAqB,EAAa,uBAAuB,CAIzD,EAAc,EACjB,MAAM,GAAiB,CACvB,IAAI,EAAO,EAAe,CAC1B,aAAc,EACd,CAAC,CAEE,EAAiB,IAAI,EAW3B,OATA,MAAM,EAAY,QAAQ,EAAe,CAEzC,MAAM,EAAY,CACjB,WAAY,IAAA,GACZ,OAAQ,EACR,OAAQ,EACR,qBACA,CAAC,CAEK,GC/DK,GACX,EACA,IACc,CACd,IAAM,EAAgB,EAAU,MAAM,KAAK,EAAU,CAC/C,EAAkB,EAAU,SAAS,KAAK,EAAU,CACpD,EAAkB,EAAU,SAAS,KAAK,EAAU,CACpD,EAAoB,EAAU,WAAW,KAAK,EAAU,CACxD,EAAe,EAAU,KAAK,KAAK,EAAU,CAC7C,EAAgB,EAAU,MAAM,KAAK,EAAU,CAqDrD,MAnDA,GAAU,MAAQ,UAChB,EAAa,CACX,KAAM,QACP,CAAC,CAEK,KAAiB,EAG1B,EAAU,QAAU,UAClB,EAAa,CACX,KAAM,UACP,CAAC,CAEK,KAAmB,EAG5B,EAAU,QAAU,KAAO,KACzB,EAAa,CACX,QACA,KAAM,UACP,CAAC,CAEK,IAAkB,EAAM,EAGjC,EAAU,UAAY,KAAO,KAC3B,EAAa,CACX,UACA,KAAM,YACP,CAAC,CAEK,IAAoB,EAAQ,EAGrC,EAAU,KAAO,KAAO,KACtB,EAAa,CACX,UACA,KAAM,OACP,CAAC,CAEK,IAAe,EAAQ,EAGhC,EAAU,MAAQ,UAChB,EAAa,CACX,KAAM,QACP,CAAC,CAEK,KAAiB,EAGnB"}
|
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
import{AccessControlBlockError as e,BUILD_TIME_CONFIG as t,Client as n,ConfigurationError as r,ProcessSpawnError as i,ProxyConnectionError as a,ReadBuffer as o,SSEClientTransport as s,Server as c,StdioServerTransport as l,StreamableHTTPClientTransport as u,TransportError as d,UnauthorizedError as f,addBreadcrumbSafe as p,addGlobalTags as m,captureExceptionSafe as h,compressClientInfo as g,config as _,createParser as v,discoverAuthorizationServerMetadata as y,discoverOAuthProtectedResourceMetadata as b,exchangeAuthorization as ee,getSessionData as te,initSentrySafe as ne,initializeTrafficMirror as re,logger as x,normalizeUrl as S,proxyServer as ie,refreshAuthorization as C,registerClient as w,serializeMessage as T,startAuthorization as E,startHTTPServer as ae}from"./normalizeUrl-CLNW7rYo.js";import{hideBin as oe,yargs_default as se}from"./helpers-DH8ZNg5N.js";import"./pkce-ANRIC6ce.js";import{join as ce}from"node:path";import{platform as le}from"node:os";import{createHash as ue}from"node:crypto";import{createServer as de}from"node:http";import{setTimeout as D}from"node:timers";import fe from"node:util";import{execFile as pe,spawn as me}from"node:child_process";import{URL as he}from"node:url";import{chmod as ge,mkdir as _e,readFile as ve,rm as ye,writeFile as be}from"node:fs/promises";import{PassThrough as xe,Transform as Se}from"node:stream";var Ce=class extends Event{constructor(e,t){super(e),this.code=t?.code??void 0,this.message=t?.message??void 0}[Symbol.for(`nodejs.util.inspect.custom`)](e,t,n){return n(Te(this),t)}[Symbol.for(`Deno.customInspect`)](e,t){return e(Te(this),t)}};function we(e){let t=globalThis.DOMException;return typeof t==`function`?new t(e,`SyntaxError`):SyntaxError(e)}function O(e){return e instanceof Error?`errors`in e&&Array.isArray(e.errors)?e.errors.map(O).join(`, `):`cause`in e&&e.cause instanceof Error?`${e}: ${O(e.cause)}`:e.message:`${e}`}function Te(e){return{type:e.type,message:e.message,code:e.code,defaultPrevented:e.defaultPrevented,cancelable:e.cancelable,timeStamp:e.timeStamp}}var Ee=e=>{throw TypeError(e)},k=(e,t,n)=>t.has(e)||Ee(`Cannot `+n),A=(e,t,n)=>(k(e,t,`read from private field`),n?n.call(e):t.get(e)),j=(e,t,n)=>t.has(e)?Ee(`Cannot add the same private member more than once`):t instanceof WeakSet?t.add(e):t.set(e,n),M=(e,t,n,r)=>(k(e,t,`write to private field`),t.set(e,n),n),N=(e,t,n)=>(k(e,t,`access private method`),n),P,F,I,L,R,z,B,V,H,U,W,G,K,q,J,Y,X,De,Z,Oe,Q,ke,Ae,$=class extends EventTarget{constructor(e,t){super(),j(this,q),this.CONNECTING=0,this.OPEN=1,this.CLOSED=2,j(this,P),j(this,F),j(this,I),j(this,L),j(this,R),j(this,z),j(this,B),j(this,V,null),j(this,H),j(this,U),j(this,W,null),j(this,G,null),j(this,K,null),j(this,Y,async e=>{var t;A(this,U).reset();let{body:n,redirected:r,status:i,headers:a}=e;if(i===204){N(this,q,Q).call(this,`Server sent HTTP 204, not reconnecting`,204),this.close();return}if(r?M(this,I,new URL(e.url)):M(this,I,void 0),i!==200){N(this,q,Q).call(this,`Non-200 status code (${i})`,i);return}if(!(a.get(`content-type`)||``).startsWith(`text/event-stream`)){N(this,q,Q).call(this,`Invalid content type, expected "text/event-stream"`,i);return}if(A(this,P)===this.CLOSED)return;M(this,P,this.OPEN);let o=new Event(`open`);if((t=A(this,K))==null||t.call(this,o),this.dispatchEvent(o),typeof n!=`object`||!n||!(`getReader`in n)){N(this,q,Q).call(this,`Invalid response body, expected a web ReadableStream`,i),this.close();return}let s=new TextDecoder,c=n.getReader(),l=!0;do{let{done:e,value:t}=await c.read();t&&A(this,U).feed(s.decode(t,{stream:!e})),e&&(l=!1,A(this,U).reset(),N(this,q,ke).call(this))}while(l)}),j(this,X,e=>{M(this,H,void 0),!(e.name===`AbortError`||e.type===`aborted`)&&N(this,q,ke).call(this,O(e))}),j(this,Z,e=>{typeof e.id==`string`&&M(this,V,e.id);let t=new MessageEvent(e.event||`message`,{data:e.data,origin:A(this,I)?A(this,I).origin:A(this,F).origin,lastEventId:e.id||``});A(this,G)&&(!e.event||e.event===`message`)&&A(this,G).call(this,t),this.dispatchEvent(t)}),j(this,Oe,e=>{M(this,z,e)}),j(this,Ae,()=>{M(this,B,void 0),A(this,P)===this.CONNECTING&&N(this,q,J).call(this)});try{if(e instanceof URL)M(this,F,e);else if(typeof e==`string`)M(this,F,new URL(e,je()));else throw Error(`Invalid URL`)}catch{throw we(`An invalid or illegal string was specified`)}M(this,U,v({onEvent:A(this,Z),onRetry:A(this,Oe)})),M(this,P,this.CONNECTING),M(this,z,3e3),M(this,R,t?.fetch??globalThis.fetch),M(this,L,t?.withCredentials??!1),N(this,q,J).call(this)}get readyState(){return A(this,P)}get url(){return A(this,F).href}get withCredentials(){return A(this,L)}get onerror(){return A(this,W)}set onerror(e){M(this,W,e)}get onmessage(){return A(this,G)}set onmessage(e){M(this,G,e)}get onopen(){return A(this,K)}set onopen(e){M(this,K,e)}addEventListener(e,t,n){let r=t;super.addEventListener(e,r,n)}removeEventListener(e,t,n){let r=t;super.removeEventListener(e,r,n)}close(){A(this,B)&&clearTimeout(A(this,B)),A(this,P)!==this.CLOSED&&(A(this,H)&&A(this,H).abort(),M(this,P,this.CLOSED),M(this,H,void 0))}};P=new WeakMap,F=new WeakMap,I=new WeakMap,L=new WeakMap,R=new WeakMap,z=new WeakMap,B=new WeakMap,V=new WeakMap,H=new WeakMap,U=new WeakMap,W=new WeakMap,G=new WeakMap,K=new WeakMap,q=new WeakSet,J=function(){M(this,P,this.CONNECTING),M(this,H,new AbortController),A(this,R)(A(this,F),N(this,q,De).call(this)).then(A(this,Y)).catch(A(this,X))},Y=new WeakMap,X=new WeakMap,De=function(){let e={mode:`cors`,redirect:`follow`,headers:{Accept:`text/event-stream`,...A(this,V)?{"Last-Event-ID":A(this,V)}:void 0},cache:`no-store`,signal:A(this,H)?.signal};return`window`in globalThis&&(e.credentials=this.withCredentials?`include`:`same-origin`),e},Z=new WeakMap,Oe=new WeakMap,Q=function(e,t){var n;A(this,P)!==this.CLOSED&&M(this,P,this.CLOSED);let r=new Ce(`error`,{code:t,message:e});(n=A(this,W))==null||n.call(this,r),this.dispatchEvent(r)},ke=function(e,t){var n;if(A(this,P)===this.CLOSED)return;M(this,P,this.CONNECTING);let r=new Ce(`error`,{code:t,message:e});(n=A(this,W))==null||n.call(this,r),this.dispatchEvent(r),M(this,B,setTimeout(A(this,Ae),A(this,z)))},Ae=new WeakMap,$.CONNECTING=0,$.OPEN=1,$.CLOSED=2;function je(){let e=`document`in globalThis?globalThis.document:void 0;return e&&typeof e==`object`&&`baseURI`in e&&typeof e.baseURI==`string`?e.baseURI:void 0}var Me=class{client;enabled;lastBlockReason;serverName;constructor(e){this.enabled=e.enabled,this.client=e.client,this.serverName=e.serverName,x.info(`AccessControlAuthorizer initialized`,{enabled:this.enabled,hasClient:!!this.client,serverName:this.serverName})}getBlockReason(){return this.lastBlockReason?this.lastBlockReason:`MCP server${this.serverName?` '${this.serverName}'`:``} is not authorized for use in your organization and has been blocked by Onyx.`}async isAllowed(){if(!this.enabled)return!0;if(this.client)try{let e=await this.client.authorize();return e.action===`block`?(e.reason?this.lastBlockReason=e.reason:this.lastBlockReason=`MCP server${this.serverName?` '${this.serverName}'`:``} is not authorized for use in your organization and has been blocked by Onyx.`,!1):!0}catch(e){return x.error(`Access control authorization failed with unexpected error`,{error:String(e)}),!0}return x.warn(`No access control client configured, allowing by default`),!0}},Ne=class{clientInfoBase64;config;constructor(e){this.config=e,this.clientInfoBase64=this.getClientInfoBase64(),x.info(`AccessControlClient initialized`,{timeoutMs:e.timeoutMs,url:e.url})}async authorize(){try{let e=await this.sendAuthorizeRequest();return x.debug(`Access control check successful`,{action:e.action}),e}catch(e){return x.warn(`Access control check failed, failing open (allowing by default)`,{error:String(e)}),{action:`allow`}}}getClientInfoBase64(){return g(this.config.sessionData)}async sendAuthorizeRequest(){let e=new AbortController,t=setTimeout(()=>e.abort(),this.config.timeoutMs);try{let t=`${this.config.url}/${this.config.apiKey}/mcp/${this.clientInfoBase64}`,n=await fetch(t,{headers:{...this.config.headers},method:`POST`,signal:e.signal});if(!n.ok)throw Error(`Access control service returned ${n.status}: ${n.statusText}`);let r=await n.json();if(!r.action||![`allow`,`block`].includes(r.action))throw Error(`Invalid access control response format: action="${r.action}"`);return r}catch(e){throw e instanceof Error&&e.name===`AbortError`?Error(`Access control check timed out after ${this.config.timeoutMs}ms`):e}finally{clearTimeout(t)}}};async function Pe(e){let t=e.toString(),n=le(),r=Re(t);return new Promise((e,i)=>{let a,o;if(n===`win32`){let e=`Start-Process ${Ie(t)}`,n=Fe(e);a=Le(),o=[`-NoProfile`,`-NonInteractive`,`-ExecutionPolicy`,`Bypass`,`-EncodedCommand`,n]}else n===`darwin`?(a=`open`,o=[t]):(a=`xdg-open`,o=[t]);pe(a,o,(t,o,s)=>{if(t){let e={command:a,os:n,stderr:s,url:r};x.warn(`Failed to open browser automatically`,{...e,error:t.message}),h(t,e,{feature:`auth`,module:`browser`,operation:`openBrowser`});let o=Error(`Failed to open browser: ${t.message} (command: ${a}, os: ${n}, url: ${r})`);o.cause=t,i(o)}else x.debug(`Browser opened successfully`,{url:r}),e()})})}function Fe(e){return Buffer.from(e,`utf16le`).toString(`base64`)}function Ie(e){return`'${e.replaceAll(`'`,`''`)}'`}function Le(){return`${process.env.SYSTEMROOT||process.env.windir||`C:\\Windows`}\\System32\\WindowsPowerShell\\v1.0\\powershell.exe`}function Re(e){try{let t=new URL(e);return t.search?`${t.protocol}//${t.host}${t.pathname}?[REDACTED]`:`${t.protocol}//${t.host}${t.pathname}`}catch{return`[INVALID_URL]`}}const ze=49152;function Be(){return Math.floor(Math.random()*(65535-ze+1))+ze}function Ve(e,t){return new Promise((n,r)=>{let i=t=>{e.removeListener(`listening`,a),r(t)},a=()=>{e.removeListener(`error`,i),n()};e.once(`error`,i),e.once(`listening`,a),e.listen(t,`127.0.0.1`)})}async function He(e={}){let{maxAttempts:t=5,preferredPort:n,timeoutMs:r=3e5}=e,i,a,o={},s=new Promise((e,t)=>{i=e,a=t}),c,l=de((e,t)=>{if(e.url===`/favicon.ico`){t.writeHead(404),t.end();return}if(!e.url?.startsWith(`/callback`)){t.writeHead(404),t.end(`Not Found`);return}try{let n=new he(e.url,`http://localhost:${c}`),r=n.searchParams.get(`code`),s=n.searchParams.get(`error`),u=n.searchParams.get(`error_description`),d=n.searchParams.get(`state`);if(s){x.error(`OAuth authorization error`,{error:s,errorDescription:u}),t.writeHead(400,{"Content-Type":`text/html`}),t.end(We(s,u||void 0)),clearTimeout(o.id),a(Error(`OAuth authorization failed: ${s}${u?` - ${u}`:``}`));return}if(!r){x.error(`OAuth callback missing authorization code`),t.writeHead(400,{"Content-Type":`text/html`}),t.end(We(`missing_code`,`No authorization code was provided`)),clearTimeout(o.id),a(Error(`OAuth callback missing authorization code`));return}x.info(`OAuth authorization code received`,{codePrefix:`${r.substring(0,10)}...`,hasState:!!d}),t.writeHead(200,{"Content-Type":`text/html`}),t.end(`<!DOCTYPE html>
|
|
2
|
+
<html lang="en">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="UTF-8">
|
|
5
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
6
|
+
<title>Authorization Successful</title>
|
|
7
|
+
<style>
|
|
8
|
+
body {
|
|
9
|
+
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
|
|
10
|
+
display: flex;
|
|
11
|
+
justify-content: center;
|
|
12
|
+
align-items: center;
|
|
13
|
+
min-height: 100vh;
|
|
14
|
+
margin: 0;
|
|
15
|
+
background: linear-gradient(135deg, #1a1a2e 0%, #16213e 100%);
|
|
16
|
+
color: #fff;
|
|
17
|
+
}
|
|
18
|
+
.container {
|
|
19
|
+
text-align: center;
|
|
20
|
+
padding: 2rem;
|
|
21
|
+
}
|
|
22
|
+
.checkmark {
|
|
23
|
+
width: 80px;
|
|
24
|
+
height: 80px;
|
|
25
|
+
border-radius: 50%;
|
|
26
|
+
background: #10b981;
|
|
27
|
+
display: flex;
|
|
28
|
+
align-items: center;
|
|
29
|
+
justify-content: center;
|
|
30
|
+
margin: 0 auto 1.5rem;
|
|
31
|
+
animation: pop 0.3s ease-out;
|
|
32
|
+
}
|
|
33
|
+
.checkmark svg {
|
|
34
|
+
width: 40px;
|
|
35
|
+
height: 40px;
|
|
36
|
+
stroke: white;
|
|
37
|
+
stroke-width: 3;
|
|
38
|
+
}
|
|
39
|
+
h1 {
|
|
40
|
+
margin: 0 0 0.5rem;
|
|
41
|
+
font-size: 1.75rem;
|
|
42
|
+
font-weight: 600;
|
|
43
|
+
}
|
|
44
|
+
p {
|
|
45
|
+
margin: 0;
|
|
46
|
+
color: #a0aec0;
|
|
47
|
+
font-size: 1rem;
|
|
48
|
+
}
|
|
49
|
+
@keyframes pop {
|
|
50
|
+
0% { transform: scale(0); }
|
|
51
|
+
50% { transform: scale(1.1); }
|
|
52
|
+
100% { transform: scale(1); }
|
|
53
|
+
}
|
|
54
|
+
</style>
|
|
55
|
+
</head>
|
|
56
|
+
<body>
|
|
57
|
+
<div class="container">
|
|
58
|
+
<div class="checkmark">
|
|
59
|
+
<svg viewBox="0 0 24 24" fill="none">
|
|
60
|
+
<path d="M5 13l4 4L19 7" stroke-linecap="round" stroke-linejoin="round"/>
|
|
61
|
+
</svg>
|
|
62
|
+
</div>
|
|
63
|
+
<h1>Authorization Successful</h1>
|
|
64
|
+
<p>You can close this window and return to the terminal.</p>
|
|
65
|
+
</div>
|
|
66
|
+
<script>setTimeout(() => window.close(), 3000);<\/script>
|
|
67
|
+
</body>
|
|
68
|
+
</html>`),clearTimeout(o.id),i({code:r,state:d||void 0}),setTimeout(()=>l.close(),1e3)}catch(e){x.error(`Error processing OAuth callback`,{error:String(e)}),t.writeHead(500),t.end(`Internal Server Error`),clearTimeout(o.id),a(e instanceof Error?e:Error(String(e)))}});o.id=setTimeout(()=>{a(Error(`OAuth callback timeout after ${r}ms`)),l.close()},r);let u=[],d;for(;u.length<t;){let e;if(u.length===0&&n!==void 0)e=n;else for(e=Be();u.includes(e);)e=Be();u.push(e);try{await Ve(l,e),c=e;let t=`http://localhost:${c}/callback`;return x.info(`OAuth callback server started`,{attempt:u.length,callbackUrl:t,port:c}),{callbackUrl:t,close:()=>new Promise(e=>{clearTimeout(o.id),l.close(()=>e())}),port:c,waitForCallback:()=>s}}catch(n){if(n.code===`EADDRINUSE`)x.debug(`Port in use, trying another port`,{attempt:u.length,maxAttempts:t,port:e}),d=Error(`Port ${e} is already in use`);else throw clearTimeout(o.id),l.close(),n}}throw clearTimeout(o.id),l.close(),Error(`Failed to start OAuth callback server after ${t} attempts. Tried ports: ${u.join(`, `)}. Last error: ${d?.message||`unknown`}`)}function Ue(e){return e.replace(/&/g,`&`).replace(/</g,`<`).replace(/>/g,`>`).replace(/"/g,`"`).replace(/'/g,`'`)}function We(e,t){return`<!DOCTYPE html>
|
|
69
|
+
<html lang="en">
|
|
70
|
+
<head>
|
|
71
|
+
<meta charset="UTF-8">
|
|
72
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
73
|
+
<title>Authorization Failed</title>
|
|
74
|
+
<style>
|
|
75
|
+
body {
|
|
76
|
+
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
|
|
77
|
+
display: flex;
|
|
78
|
+
justify-content: center;
|
|
79
|
+
align-items: center;
|
|
80
|
+
min-height: 100vh;
|
|
81
|
+
margin: 0;
|
|
82
|
+
background: linear-gradient(135deg, #1a1a2e 0%, #16213e 100%);
|
|
83
|
+
color: #fff;
|
|
84
|
+
}
|
|
85
|
+
.container {
|
|
86
|
+
text-align: center;
|
|
87
|
+
padding: 2rem;
|
|
88
|
+
max-width: 400px;
|
|
89
|
+
}
|
|
90
|
+
.error-icon {
|
|
91
|
+
width: 80px;
|
|
92
|
+
height: 80px;
|
|
93
|
+
border-radius: 50%;
|
|
94
|
+
background: #ef4444;
|
|
95
|
+
display: flex;
|
|
96
|
+
align-items: center;
|
|
97
|
+
justify-content: center;
|
|
98
|
+
margin: 0 auto 1.5rem;
|
|
99
|
+
}
|
|
100
|
+
.error-icon svg {
|
|
101
|
+
width: 40px;
|
|
102
|
+
height: 40px;
|
|
103
|
+
stroke: white;
|
|
104
|
+
stroke-width: 3;
|
|
105
|
+
}
|
|
106
|
+
h1 {
|
|
107
|
+
margin: 0 0 0.5rem;
|
|
108
|
+
font-size: 1.75rem;
|
|
109
|
+
font-weight: 600;
|
|
110
|
+
}
|
|
111
|
+
.error-code {
|
|
112
|
+
color: #f87171;
|
|
113
|
+
font-family: monospace;
|
|
114
|
+
margin-bottom: 0.5rem;
|
|
115
|
+
}
|
|
116
|
+
p {
|
|
117
|
+
margin: 0;
|
|
118
|
+
color: #a0aec0;
|
|
119
|
+
font-size: 1rem;
|
|
120
|
+
}
|
|
121
|
+
</style>
|
|
122
|
+
</head>
|
|
123
|
+
<body>
|
|
124
|
+
<div class="container">
|
|
125
|
+
<div class="error-icon">
|
|
126
|
+
<svg viewBox="0 0 24 24" fill="none">
|
|
127
|
+
<path d="M6 18L18 6M6 6l12 12" stroke-linecap="round" stroke-linejoin="round"/>
|
|
128
|
+
</svg>
|
|
129
|
+
</div>
|
|
130
|
+
<h1>Authorization Failed</h1>
|
|
131
|
+
<p class="error-code">${Ue(e)}</p>
|
|
132
|
+
${t?`<p>${Ue(t)}</p>`:``}
|
|
133
|
+
</div>
|
|
134
|
+
</body>
|
|
135
|
+
</html>`}var Ge=class{storageDir;constructor(e){this.storageDir=e.storageDir}async delete(e){let t=S(e),n=this.getFilePath(t);try{await ye(n),x.debug(`Deleted stored credentials`,{filePath:n,serverUrl:t})}catch(e){e.code!==`ENOENT`&&x.warn(`Failed to delete stored credentials`,{error:String(e),filePath:n,serverUrl:t})}}hasRefreshToken(e){return!!e.tokens?.refresh_token}hasValidAccessToken(e){return!!e.tokens?.access_token&&!this.isAccessTokenExpired(e)}isAccessTokenExpired(e){if(!e.tokens||!e.tokensObtainedAt)return!0;let{expires_in:t}=e.tokens;if(!t)return!1;let n=new Date(e.tokensObtainedAt).getTime()+t*1e3,r=Date.now(),i=r>=n-3e4;return x.debug(`Checked access token expiration`,{expiresAt:new Date(n).toISOString(),expiresIn:t,isExpired:i,now:new Date(r).toISOString(),obtainedAt:e.tokensObtainedAt}),i}async load(e){let t=S(e),n=this.getFilePath(t);try{let e=await ve(n,`utf-8`),r=JSON.parse(e);if(S(r.serverUrl)!==t){x.warn(`Credential file server URL mismatch`,{expected:t,filePath:n,found:r.serverUrl});return}return x.debug(`Loaded stored credentials`,{clientId:r.clientInfo?.client_id,hasRefreshToken:!!r.tokens?.refresh_token,hasTokens:!!r.tokens,serverUrl:t}),r}catch(e){if(e.code===`ENOENT`){x.debug(`No stored credentials found`,{serverUrl:t});return}x.warn(`Failed to load stored credentials`,{error:String(e),filePath:n,serverUrl:t});return}}async save(e){await this.ensureStorageDir();let t=this.getFilePath(e.serverUrl),n=JSON.stringify(e,null,2);try{await be(t,n,{encoding:`utf-8`,mode:384}),await ge(t,384),x.debug(`Saved credentials to disk`,{clientId:e.clientInfo?.client_id,filePath:t,hasTokens:!!e.tokens,serverUrl:e.serverUrl})}catch(n){throw x.error(`Failed to save credentials`,{error:String(n),filePath:t,serverUrl:e.serverUrl}),n}}async update(e,t){let n=S(e),r=await this.load(n),i=new Date().toISOString(),a=r?{...r,...t,serverUrl:n,updatedAt:i}:{createdAt:i,serverUrl:n,updatedAt:i,...t};return await this.save(a),a}async ensureStorageDir(){try{await _e(this.storageDir,{mode:448,recursive:!0})}catch(e){if(e.code!==`EEXIST`)throw e}}generateKey(e){return ue(`sha256`).update(e).digest(`hex`).substring(0,16)}getFilePath(e){let t=this.generateKey(e);return ce(this.storageDir,`${t}.json`)}};const Ke=t.OAUTH_PROVIDERS;function qe(e){let t=typeof e==`string`?new URL(e):e,n=t.hostname.toLowerCase();for(let e of Ke)for(let t of e.urlPatterns){let r=t.toLowerCase();if(Je(n,r))return x.debug(`Found matching OAuth provider for URL`,{hostname:n,pattern:t,providerId:e.id,providerName:e.name}),e}x.debug(`No pre-configured OAuth provider found for URL`,{hostname:n,serverUrl:t.toString()})}function Je(e,t){return!!(e===t||e.endsWith(`.${t}`))}async function Ye(e,t={}){let{credentialStore:n,scope:r,timeoutMs:i}=t,a=S(e),o=new URL(a);if(x.info(`Starting OAuth flow`,{serverUrl:a}),n){let e=await Qe(o,n);if(e)return x.info(`Using existing OAuth credentials`,{clientId:e.clientInfo.client_id,serverUrl:a}),e}x.debug(`Starting OAuth callback server...`);let s=await He({timeoutMs:i}),c=s.callbackUrl;x.info(`OAuth callback server started`,{callbackUrl:c,port:s.port});try{let e=qe(o);if(e)return x.info(`Using pre-configured OAuth provider`,{providerId:e.id,providerName:e.name}),await Ze(o,e,s,n,r);x.debug(`Discovering OAuth protected resource metadata...`);let i;try{i=await b(o),x.info(`Discovered protected resource metadata`,{authorizationServers:i?.authorization_servers,resource:i?.resource,scopesSupported:i?.scopes_supported})}catch(e){x.warn(`Could not discover protected resource metadata, will try direct discovery`,{error:String(e)})}let l;i?.authorization_servers?.length?(l=new URL(i.authorization_servers[0]),x.debug(`Using authorization server from resource metadata`,{authServerUrl:l.toString()})):(l=o,x.debug(`Using server URL as authorization server`,{authServerUrl:l.toString()})),x.debug(`Discovering authorization server metadata...`);let u=await y(l);if(!u)throw Error(`Could not discover OAuth metadata from ${l}. The server may not support OAuth 2.0 or the metadata endpoint is not accessible.`);x.info(`Discovered authorization server metadata`,{authorizationEndpoint:u.authorization_endpoint,issuer:u.issuer,registrationEndpoint:u.registration_endpoint,tokenEndpoint:u.token_endpoint});let d,f=n?await n.load(a):void 0,p=f?.clientInfo?.redirect_uris||[];if(f?.clientInfo&&p.includes(c)&&f?.clientInfo)x.info(`Reusing existing DCR client registration`,{clientId:f.clientInfo.client_id,clientName:f.clientInfo.client_name,redirectUri:c}),d=f.clientInfo;else{f?.clientInfo&&x.info(`Existing DCR client has different redirect_uri, re-registering`,{existingRedirectUris:p,newRedirectUri:c});let e={client_name:t.clientMetadata?.client_name||`MCP Gateway`,grant_types:t.clientMetadata?.grant_types||[`authorization_code`,`refresh_token`],redirect_uris:[c],response_types:t.clientMetadata?.response_types||[`code`],token_endpoint_auth_method:t.clientMetadata?.token_endpoint_auth_method||`none`,...r&&{scope:r}};if(x.debug(`Client metadata for DCR`,{clientMetadata:e}),!u.registration_endpoint)throw Error(`Authorization server does not support Dynamic Client Registration. No registration_endpoint found in metadata.`);x.info(`Registering client via DCR...`,{registrationEndpoint:u.registration_endpoint}),d=await w(l,{clientMetadata:e,metadata:u}),x.info(`Client registered successfully via DCR`,{clientId:d.client_id,clientName:d.client_name,clientSecretExpiresAt:d.client_secret_expires_at}),n&&await n.update(a,{clientInfo:d})}let m=i?.resource?new URL(i.resource):void 0;x.debug(`Starting authorization flow...`);let{authorizationUrl:h,codeVerifier:g}=await E(l,{clientInformation:d,metadata:u,redirectUrl:c,resource:m,scope:r});await Pe(h),x.debug(`Waiting for OAuth callback...`);let _=await s.waitForCallback();x.info(`Authorization code received`,{codePrefix:`${_.code.substring(0,10)}...`,hasState:!!_.state}),x.debug(`Exchanging authorization code for tokens...`);let v=await ee(l,{authorizationCode:_.code,clientInformation:d,codeVerifier:g,metadata:u,redirectUri:c,resource:m});if(x.info(`OAuth tokens obtained successfully`,{expiresIn:v.expires_in,hasRefreshToken:!!v.refresh_token,tokenType:v.token_type}),n){let e=new Date().toISOString();await n.update(a,{tokens:v,tokensObtainedAt:e}),x.info(`OAuth credentials persisted to disk`,{serverUrl:a})}return{clientInfo:d,fromCache:!1,tokens:v}}finally{await s.close()}}async function Xe(e,t,n,r){let i=e.tokenEndpoint,a=e.tokenEndpointAuthMethod||(e.clientSecret?`client_secret_post`:`none`),o=new URLSearchParams;o.set(`grant_type`,`authorization_code`),o.set(`code`,t),o.set(`redirect_uri`,n),r&&o.set(`code_verifier`,r);let s={Accept:`application/json`,"Content-Type":`application/x-www-form-urlencoded`};a===`client_secret_basic`&&e.clientSecret?s.Authorization=`Basic ${Buffer.from(`${e.clientId}:${e.clientSecret}`).toString(`base64`)}`:a===`client_secret_post`&&e.clientSecret?(o.set(`client_id`,e.clientId),o.set(`client_secret`,e.clientSecret)):o.set(`client_id`,e.clientId),x.debug(`Sending token request`,{authMethod:a,providerId:e.id,tokenEndpoint:i});let c=await fetch(i,{body:o.toString(),headers:s,method:`POST`});if(!c.ok){let t=await c.text();throw x.error(`Token exchange failed`,{error:t,providerId:e.id,status:c.status}),Error(`Token exchange failed: ${c.status} ${t}`)}let l=c.headers.get(`content-type`)||``,u;if(l.includes(`application/json`))u=await c.json();else if(l.includes(`application/x-www-form-urlencoded`)||l.includes(`text/plain`)){let e=await c.text(),t=new URLSearchParams(e);u=Object.fromEntries(t.entries())}else{let e=await c.text();try{u=JSON.parse(e)}catch{let t=new URLSearchParams(e);u=Object.fromEntries(t.entries())}}return typeof u.expires_in==`string`&&(u.expires_in=parseInt(u.expires_in,10)),{access_token:u.access_token,expires_in:u.expires_in,refresh_token:u.refresh_token,scope:u.scope,token_type:u.token_type||`bearer`}}async function Ze(e,t,n,r,i){let a=e.toString(),o=n.callbackUrl,s={client_id:t.clientId,client_name:t.name,client_secret:t.clientSecret,redirect_uris:[o],token_endpoint_auth_method:t.tokenEndpointAuthMethod||(t.clientSecret?`client_secret_post`:`none`)};x.info(`Using pre-configured OAuth client`,{clientId:t.clientId,hasClientSecret:!!t.clientSecret,providerId:t.id,tokenEndpointAuthMethod:s.token_endpoint_auth_method});let c=i||t.scopes?.join(` `),l=t.usePkce!==!1,u,d,f;if(l){let{generateCodeChallenge:e,generateCodeVerifier:t}=await import(`./pkce-dymop6yt.js`);u=t(),d=await e(u),f=`S256`}let p=new URL(t.authorizationEndpoint);p.searchParams.set(`client_id`,t.clientId),p.searchParams.set(`redirect_uri`,o),p.searchParams.set(`response_type`,`code`),c&&p.searchParams.set(`scope`,c);let m=crypto.randomUUID();p.searchParams.set(`state`,m),d&&f&&(p.searchParams.set(`code_challenge`,d),p.searchParams.set(`code_challenge_method`,f)),await Pe(p),x.debug(`Waiting for OAuth callback...`);let h=await n.waitForCallback();if(h.state!==m)throw Error(`OAuth state mismatch - possible CSRF attack`);x.info(`Authorization code received`,{codePrefix:`${h.code.substring(0,10)}...`,providerId:t.id}),x.debug(`Exchanging authorization code for tokens...`);let g=await Xe(t,h.code,o,u);if(x.info(`OAuth tokens obtained successfully`,{expiresIn:g.expires_in,hasRefreshToken:!!g.refresh_token,providerId:t.id,tokenType:g.token_type}),r){let e=new Date().toISOString();await r.update(a,{clientInfo:s,tokens:g,tokensObtainedAt:e}),x.info(`OAuth credentials persisted to disk`,{providerId:t.id,serverUrl:a})}return{clientInfo:s,fromCache:!1,tokens:g}}async function Qe(e,t){let n=await t.load(e.toString());if(!n){x.debug(`No stored credentials found`,{serverUrl:e.toString()});return}if(!n.clientInfo){x.debug(`Stored credentials have no client info`,{serverUrl:e.toString()});return}if(t.hasValidAccessToken(n)&&n.tokens)return x.info(`Using cached OAuth credentials with valid access token`,{clientId:n.clientInfo.client_id,serverUrl:e.toString()}),{clientInfo:n.clientInfo,fromCache:!0,tokens:n.tokens};if(t.hasRefreshToken(n)){let r=await $e(e,n,t);if(r)return r}x.debug(`Stored credentials are expired and no valid refresh token`,{serverUrl:e.toString()})}async function $e(e,t,n){if(!(!t.clientInfo||!t.tokens?.refresh_token)){x.info(`Attempting to refresh expired access token`,{clientId:t.clientInfo.client_id,serverUrl:e.toString()});try{let r;try{r=await b(e)}catch{}let i;i=r?.authorization_servers?.length?new URL(r.authorization_servers[0]):e;let a=await y(i),o=r?.resource?new URL(r.resource):void 0,s=await C(i,{clientInformation:t.clientInfo,metadata:a,refreshToken:t.tokens.refresh_token,resource:o});x.info(`Successfully refreshed access token`,{clientId:t.clientInfo.client_id,expiresIn:s.expires_in,hasNewRefreshToken:!!s.refresh_token});let c=new Date().toISOString();return await n.update(e.toString(),{tokens:s,tokensObtainedAt:c}),{clientInfo:t.clientInfo,fromCache:!0,tokens:s}}catch(t){x.warn(`Failed to refresh access token, will require re-authorization`,{error:String(t),serverUrl:e.toString()}),await n.update(e.toString(),{tokens:void 0,tokensObtainedAt:void 0});return}}}const et=[`streamable-http`,`sse`];async function tt(e,t,n){let r;for(let i of et){let a=e();try{let e=nt(t,i,n);return await a.connect(e),{client:a,transportType:i}}catch(e){if(r=e,rt(e)){x.info(`${i} not supported, trying next transport...`);continue}throw e}}throw r??Error(`No compatible transport found`)}function nt(e,t,n){let r=new URL(e);return t===`streamable-http`?new u(r,{requestInit:n}):new s(r,{requestInit:n})}function rt(e){if(!e||typeof e!=`object`)return!1;let t=e.status,n=e.code,r=e.message;return!!(t===404||t===405||n===404||n===405||r&&/404|not found|405|method not allowed/i.test(String(r)))}var it=class extends Se{buffer=``;constructor(){super({objectMode:!1})}_flush(e){this.buffer.trim().startsWith(`{`)?e(null,Buffer.from(this.buffer)):e(null,null)}_transform(e,t,n){this.buffer+=e.toString();let r=this.buffer.split(`
|
|
136
|
+
`);this.buffer=r.pop()||``;let i=r.filter(e=>e.trim().startsWith(`{`));if(i.length>0){let e=`${i.join(`
|
|
137
|
+
`)}\n`;n(null,Buffer.from(e))}else n(null,null)}},at=class{onclose;onerror;onmessage;get pid(){return this._process?.pid??null}get stderr(){return this._stderrStream?this._stderrStream:this._process?.stderr??null}_abortController=new AbortController;_process;_readBuffer=new o;_serverParams;_stderrStream=null;onEvent;constructor(e){this._serverParams=e,(e.stderr===`pipe`||e.stderr===`overlapped`)&&(this._stderrStream=new xe),this.onEvent=e.onEvent}async close(){this.onEvent?.({type:`close`}),this._abortController.abort(),this._process=void 0,this._readBuffer.clear()}send(e){return new Promise(t=>{if(!this._process?.stdin)throw Error(`Not connected`);let n=T(e);this._process.stdin.write(n)?t():this._process.stdin.once(`drain`,t)})}async start(){if(this._process)throw Error(`StdioClientTransport already started! If using Client class, note that connect() calls start() automatically.`);return new Promise((e,t)=>{this._process=me(this._serverParams.command,this._serverParams.args??[],{cwd:this._serverParams.cwd,env:this._serverParams.env,shell:this._serverParams.shell??!1,signal:this._abortController.signal,stdio:[`pipe`,`pipe`,this._serverParams.stderr??`inherit`]}),this._process.on(`error`,e=>{if(e.name===`AbortError`){this.onclose?.();return}let n=new i(`Process spawn error: ${e.message}`,this._serverParams.command,this._serverParams.args,{cwd:this._serverParams.cwd,errorCode:e.code,errorErrno:e.errno,errorSyscall:e.syscall,shell:this._serverParams.shell});h(n),t(e),this.onerror?.(e)}),this._process.on(`spawn`,()=>{p(`Process spawned successfully`,`transport`,`info`,{argCount:this._serverParams.args?.length,command:this._serverParams.command,pid:this._process?.pid}),e()}),this._process.on(`close`,e=>{this.onEvent?.({type:`close`}),this._process=void 0,this.onclose?.()}),this._process.stdin?.on(`error`,e=>{let t=new d(`Stdin error: ${e.message}`,`stdio`,`write`,{command:this._serverParams.command,pid:this._process?.pid});h(t),this.onEvent?.({error:e,type:`error`}),this.onerror?.(e)});let n=new it;this._process.stdout?.pipe(n),n.on(`data`,e=>{this.onEvent?.({chunk:e.toString(),type:`data`}),this._readBuffer.append(e),this.processReadBuffer()}),n.on(`error`,e=>{let t=new d(`JSON filter error: ${e.message}`,`stdio`,`parse`,{command:this._serverParams.command,pid:this._process?.pid});h(t),this.onEvent?.({error:e,type:`error`}),this.onerror?.(e)}),this._stderrStream&&this._process.stderr&&this._process.stderr.pipe(this._stderrStream)})}processReadBuffer(){for(;;)try{let e=this._readBuffer.readMessage();if(e===null)break;this.onEvent?.({message:e,type:`message`}),this.onmessage?.(e)}catch(e){let t=null;try{t=this._readBuffer.readMessage()}catch{}let n=new d(`Message processing error: ${e.message}`,`stdio`,`process`,{command:this._serverParams.command,messageType:t?typeof t:`unknown`,pid:this._process?.pid});h(n),this.onEvent?.({error:e,type:`error`}),this.onerror?.(e)}}};fe.inspect.defaultOptions.depth=8,`EventSource`in global||(global.EventSource=$);const ot=async()=>{let t=await se(oe(process.argv)).scriptName(`mcp-gateway`).command(`stdio [command] [args...]`,`Run a local command with stdio transport (default)`,e=>e.positional(`command`,{describe:`The command to run`,type:`string`}).positional(`args`,{array:!0,describe:`The arguments to pass to the command`,type:`string`})).command(`remote`,`Connect to a remote MCP server`,e=>e.options({headers:{describe:`Headers for remote authentication (format: key:value)`,string:!0,type:`array`},"remote-transport":{choices:[`sse`,`stream`],deprecated:`Transport is now auto-detected per MCP spec. This option is ignored.`,describe:`(Deprecated) The transport type - now auto-detected`,type:`string`},url:{demandOption:!0,describe:`The URL of the remote MCP server`,type:`string`}})).command(`$0 [command] [args...]`,`Run a command with MCP arguments (backward compatible)`,e=>e.positional(`command`,{describe:`The command to run`,type:`string`}).positional(`args`,{array:!0,describe:`The arguments to pass to the command`,type:`string`})).env(`MCP_GATEWAY`).parserConfiguration({"populate--":!0}).options({"access-control-url":{describe:`URL of the access control service (overrides MCP_GATEWAY_ACCESS_CONTROL_URL)`,type:`string`},debug:{default:!1,describe:`Enable debug logging`,type:`boolean`},env:{describe:`Environment variables to pass to the command (format: KEY=value)`,string:!0,type:`array`},gracefulShutdownTimeout:{default:5e3,describe:`The timeout (in milliseconds) for graceful shutdown`,type:`number`},port:{describe:`Run as HTTP server on the specified port instead of stdio`,type:`number`},"scanner-api-key":{describe:`API key for scanner authentication (overrides MCP_GATEWAY_SCANNER_API_KEY)`,type:`string`},"scanner-fail-open":{describe:`Allow requests when scanner is unavailable (overrides MCP_GATEWAY_SCANNER_FAIL_OPEN)`,type:`boolean`},"scanner-timeout-ms":{describe:`Scanner request timeout in milliseconds (overrides MCP_GATEWAY_SCANNER_TIMEOUT_MS)`,type:`number`},"scanner-url":{describe:`URL of the scanner/evaluator service (overrides MCP_GATEWAY_SCANNER_URL)`,type:`string`},shell:{default:process.platform===`win32`,describe:`Spawn the server via the user's shell (defaults to true on Windows)`,type:`boolean`}}).help().parseAsync(),i,o,s,u,d,g={};if(t.headers)for(let e of t.headers){let[t,...n]=e.split(`:`);t&&n.length>0&&(g[t.trim()]=n.join(`:`).trim())}let v={..._.scanner.headers};if(x.debug(`Initializing traffic scanner`,{failOpen:_.scanner.failOpen,hasApiKey:!!_.scanner.apiKey,scannerEnabled:_.scanner.enabled,scannerUrl:_.scanner.url,timeoutMs:_.scanner.timeoutMs}),t._[0]===`remote`){i=`remote`,t[`remote-transport`]&&x.warn(`The --remote-transport option is deprecated and will be ignored. Transport is now auto-detected per MCP spec (Streamable HTTP with SSE fallback).`,{providedTransport:t[`remote-transport`]});let e=t.url;if(!e)throw new r(`Remote URL is required for remote proxy type`,`url`);u=S(e),u!==e&&x.debug(`Normalized remote URL`,{normalizedUrl:u,originalUrl:e})}else{if(i=`stdio`,t[`--`]&&t[`--`].length>0){let e=t[`--`];o=e[0],s=e.slice(1)}else o=t.command,s=t.args||[];if(!o)throw new r(`No command specified for stdio proxy`,`command`)}let y={};if(t.env)for(let e of t.env){let[t,...n]=e.split(`=`);t&&n.length>0&&(y[t.trim()]=n.join(`=`).trim())}let b;if(_.accessControl.url&&_.scanner.apiKey){let e=await te(),t=i===`stdio`?{...e,proxyType:`stdio`,stdioCliArgs:{args:s,command:o,proxyType:`stdio`}}:{...e,proxyType:`remote`,remoteCliArgs:{proxyType:`remote`,url:u}};b=new Ne({apiKey:_.scanner.apiKey,headers:_.accessControl.headers,sessionData:t,timeoutMs:_.accessControl.timeoutMs,url:_.accessControl.url})}let ee=i===`remote`?u:o,C=new Me({client:b,enabled:_.accessControl.enabled,serverName:ee}),w=new Ge({storageDir:_.oauthDir}),T=()=>new n({name:`mcp-gateway`,version:_.appVersion},{capabilities:{}}),E=async n=>{if(!await C.isAllowed()){let t=C.getBlockReason();throw x.warn(`MCP server blocked by access control`,{proxyType:i,reason:t}),new e(t,`policy_block`)}if(x.info(`MCP server allowed by access control`),i===`stdio`){let e={...process.env,...y},r=new at({args:s,command:o,env:e,onEvent:e=>{t.debug&&x.debug(`Transport event`,{event:e})},shell:t.shell,stderr:`inherit`});try{return await n.connect(r),n}catch(e){throw new a(`Failed to connect to stdio process: ${e}`,void 0,`stdio`,{argCount:s?.length,command:o})}}else if(i===`remote`){let e=u,t=e=>{let t={...g};return e&&(t.Authorization=`Bearer ${e}`),Object.keys(t).length>0?{headers:t}:void 0},n=e=>{if(e instanceof f)return!0;if(e&&typeof e==`object`){let t=e.status,n=e.code,r=e.message;if(t===401||n===401||r&&/unauthorized|401/i.test(String(r)))return!0}return!1},r=async()=>{let t=await Ye(e,{credentialStore:w});return x.info(t.fromCache?`Using cached OAuth credentials`:`OAuth flow completed successfully`),t.tokens.access_token},i;try{let a=await w.load(e),o=a&&w.hasValidAccessToken(a)?a.tokens?.access_token:void 0,s=!1;try{let n=await tt(T,e,t(o));i=n.client,d=n.transportType,x.info(`Connected to remote server`,{hasAuth:!!o,transport:d})}catch(a){if(n(a)){if(s)throw a;o?(x.info(`Cached OAuth token rejected (possibly revoked), invalidating and re-authorizing...`),await w.update(e,{tokens:void 0,tokensObtainedAt:void 0})):x.info(`Server requires OAuth, starting authentication flow...`),o=await r(),s=!0;let n=await tt(T,e,t(o));i=n.client,d=n.transportType,x.info(`Connected to remote server with OAuth`,{transport:d})}else throw a}return i}catch(t){throw t instanceof a?t:new a(`Failed to connect to remote server: ${t instanceof Error?t.message:String(t)}`,e,d,{headerCount:Object.keys(g).length})}}return n},ce=async()=>{if(t.port){await re({cliArgs:i===`stdio`?{args:s,command:o,proxyType:i}:{proxyType:i,url:u},scanApiKey:_.scanner.apiKey,scanEnabled:_.scanner.enabled,scanFailOpen:_.scanner.failOpen,scanHeaders:v,scanTimeoutMs:_.scanner.timeoutMs,scanUrl:_.scanner.url||``});let e,n=await ae({createServer:async()=>{let t=T();e=await E(t);let n=e.getServerVersion(),r=e.getServerCapabilities(),i=new c(n,{capabilities:r});return ie({authorizer:C,client:e,server:i,serverCapabilities:r}),i},onClose:async t=>{e&&await e.close(),await t.close()},port:t.port});return x.info(`MCP gateway started as HTTP server`,{port:t.port,proxyType:i}),i===`stdio`?x.info(`Running command`,{args:s?.join(` `)||``,command:o}):x.info(`Remote server configured (connection deferred until client connects)`,{remoteUrl:u}),{close:()=>n.close()}}let e=T(),n=await E(e),r=n.getServerVersion(),a=n.getServerCapabilities(),f=new c(r,{capabilities:a});ie({authorizer:C,client:n,server:f,serverCapabilities:a});let p=new l;return await re({cliArgs:i===`stdio`?{args:s,command:o,proxyType:i}:{proxyType:i,url:u},scanApiKey:_.scanner.apiKey,scanEnabled:_.scanner.enabled,scanFailOpen:_.scanner.failOpen,scanHeaders:v,scanTimeoutMs:_.scanner.timeoutMs,scanUrl:_.scanner.url||``}),await f.connect(p),x.info(`MCP gateway started`,{proxyType:i}),i===`stdio`?x.info(`Running command`,{args:s?.join(` `)||``,command:o}):x.info(`Connected to remote server`,{remoteUrl:u,transport:d}),{close:()=>n.close()}},le=({server:e,timeout:t})=>{let n=async()=>{x.info(`Received shutdown signal; shutting down`);try{await e.close(),process.exit(0)}catch(e){let n=`Error during shutdown: ${e}`;x.error(`Error during shutdown`,{error:n}),h(e instanceof Error?e:Error(n),{shutdownTimeout:t},{feature:`shutdown`,module:`cli`,operation:`close`}),process.exit(1)}},r=async()=>{D(()=>{x.error(`Graceful shutdown timeout exceeded, forcing exit`),process.exit(1)},t).unref(),await n()};return process.once(`SIGTERM`,r),process.once(`SIGINT`,r),()=>e.close()};ne(),m({"proxy.mode":i===`stdio`?`local`:`remote`,"proxy.transport":i===`remote`?`auto-detect`:`stdio`,"proxy.type":i}),p(`MCP Gateway starting`,`lifecycle`,`info`,{command:o,proxyType:i,transport:i===`remote`?`auto-detect`:`stdio`,url:u});try{let e=await ce();le({server:e,timeout:t.gracefulShutdownTimeout})}catch(t){if(t instanceof e){console.error(`
|
|
138
|
+
❌ ACCESS DENIED`),console.error(`━`.repeat(60)),console.error(`
|
|
139
|
+
This MCP server has been blocked by Onyx because it is not authorized by your organization's access control policy. Please contact your administrator if you believe this is an error.
|
|
140
|
+
`),x.error(`MCP server blocked by access control policy`,{error:t.message,reason:t.reason}),D(()=>{process.exit(1)},1e3);return}let n=`Could not start the proxy: ${t}`;x.error(`Unhandled error occurred`,{error:n}),t instanceof Error?h(t,{args:s,command:o,proxyType:i,transport:d,url:u},{feature:`startup`,module:`cli`,operation:`initialize`,proxyType:i,transport:i===`remote`?d||`unknown`:`stdio`}):h(Error(n),{command:o,originalError:String(t),proxyType:i},{feature:`startup`,module:`cli`,operation:`initialize`}),D(()=>{process.exit(1)},1e3)}};export{ot as main};
|
|
141
|
+
//# sourceMappingURL=main-xAo2ko_0.js.map
|