somamcp 1.5.1 → 2.0.0

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/index.js CHANGED
@@ -1,62 +1 @@
1
- import{n as e}from"./backend-D0qSyfXw.js";import{Map as t,Option as n,Ref as r,Set as i,Try as a,Tuple as o}from"functype";import{Client as s}from"@modelcontextprotocol/sdk/client/index.js";import{StreamableHTTPClientTransport as c}from"@modelcontextprotocol/sdk/client/streamableHttp.js";import{appendFile as l,mkdir as u,readFile as d}from"node:fs/promises";import{dirname as f,extname as p}from"node:path";import{createDirectConsoleLogger as m}from"functype-log";import{z as h}from"zod";const g=e=>e.json({error:`Unauthorized`},401),_=({authenticate:e,onUnauthorized:t})=>{let r=t??g;return async(t,i)=>{if(!e)return r(t);let o=n(t.env).flatMap(e=>n(e.incoming)).orElse(t.req.raw);if((await a.fromPromise(e(o))).isFailure())return r(t);await i()}},v=(e,t)=>typeof e.get==`function`?n(e.get(t)):n.none(),y=(e,t,r)=>{let i=e;return n(i[t]??i[r]).map(e=>Array.isArray(e)?e[0]??``:e)},b=(e,t,r)=>v(e,t).fold(()=>y(e,t,r),e=>n(e)),x=e=>n(e).flatMap(e=>n(e.headers)),S=(e,t)=>{let n=t.toLowerCase();return x(e).flatMap(e=>b(e,n,t)).orUndefined()},C=(e,t,n)=>{t.forEach(t=>{switch(t.protected&&e.use(t.path,_({authenticate:n})),t.type){case`static`:e.get(t.path,e=>e.body(t.content,{headers:{"Content-Type":t.contentType}}));break;case`dynamic`:e.get(t.path,e=>t.handler(e))}})},w=(e,t,n)=>({handler:()=>{let r=e(),i=t(),a=n(),o=T(r,i,a);return new Response(o,{headers:{"Content-Type":`text/html; charset=utf-8`}})},path:`/dashboard`,protected:!0,type:`dynamic`}),T=(e,t,n)=>`<!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>${e.name} - SomaMCP Dashboard</title>
7
- <style>
8
- * { margin: 0; padding: 0; box-sizing: border-box; }
9
- body { font-family: system-ui, -apple-system, sans-serif; background: #0a0a0a; color: #e0e0e0; padding: 2rem; }
10
- h1 { font-size: 1.5rem; margin-bottom: 1.5rem; color: #fff; }
11
- h2 { font-size: 1.1rem; margin-bottom: 0.75rem; color: #a0a0a0; text-transform: uppercase; letter-spacing: 0.05em; }
12
- .grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(300px, 1fr)); gap: 1.5rem; }
13
- .card { background: #1a1a1a; border: 1px solid #2a2a2a; border-radius: 8px; padding: 1.25rem; }
14
- .status { display: inline-block; padding: 2px 8px; border-radius: 4px; font-size: 0.85rem; font-weight: 600; }
15
- .status.running { background: #0a3d0a; color: #4ade80; }
16
- .status.stopped { background: #3d0a0a; color: #f87171; }
17
- .status.error { background: #3d2a0a; color: #fbbf24; }
18
- .status.connected { background: #0a3d0a; color: #4ade80; }
19
- .status.disconnected { background: #3d0a0a; color: #f87171; }
20
- .metric { margin: 0.5rem 0; }
21
- .metric-label { color: #888; font-size: 0.85rem; }
22
- .metric-value { color: #fff; font-weight: 500; }
23
- ul { list-style: none; }
24
- li { padding: 0.35rem 0; border-bottom: 1px solid #222; font-size: 0.9rem; }
25
- li:last-child { border-bottom: none; }
26
- .desc { color: #888; font-size: 0.8rem; }
27
- </style>
28
- </head>
29
- <body>
30
- <h1>${e.name}</h1>
31
- <div class="grid">
32
- <div class="card">
33
- <h2>Health</h2>
34
- <div class="metric"><span class="metric-label">Status:</span> <span class="status ${e.status}">${e.status}</span></div>
35
- <div class="metric"><span class="metric-label">Uptime:</span> <span class="metric-value">${E(e.uptime)}</span></div>
36
- <div class="metric"><span class="metric-label">Sessions:</span> <span class="metric-value">${e.activeSessions}</span></div>
37
- <div class="metric"><span class="metric-label">Gateways:</span> <span class="metric-value">${e.gateways.connected}/${e.gateways.total}</span></div>
38
- </div>
39
- <div class="card">
40
- <h2>Tools (${t.tools.length})</h2>
41
- <ul>${t.tools.map(e=>`<li><strong>${e.name}</strong>${e.description?` <span class="desc">${e.description}</span>`:``}</li>`).join(``)}</ul>
42
- </div>
43
- <div class="card">
44
- <h2>Resources (${t.resources.length})</h2>
45
- <ul>${t.resources.map(e=>`<li><strong>${e.name}</strong> <span class="desc">${e.uri}</span></li>`).join(``)}</ul>
46
- </div>
47
- <div class="card">
48
- <h2>Prompts (${t.prompts.length})</h2>
49
- <ul>${t.prompts.map(e=>`<li><strong>${e.name}</strong>${e.description?` <span class="desc">${e.description}</span>`:``}</li>`).join(``)}</ul>
50
- </div>
51
- ${n.length>0?`<div class="card">
52
- <h2>Gateways (${n.length})</h2>
53
- <ul>${n.map(e=>`<li><strong>${e.name}</strong> <span class="status ${e.status}">${e.status}</span> <span class="desc">${e.url}</span></li>`).join(``)}</ul>
54
- </div>`:``}
55
- </div>
56
- </body>
57
- </html>`,E=e=>{let t=Math.floor(e/1e3),n=Math.floor(t/60),r=Math.floor(n/60),i=Math.floor(r/24);return i>0?`${String(i)}d ${String(r%24)}h`:r>0?`${String(r)}h ${String(n%60)}m`:n>0?`${String(n)}m ${String(t%60)}s`:`${String(t)}s`},D=`/health`,O=`/health/detail`,ee=(e,t=D)=>({handler:()=>{let t=e(),n=t.status===`running`?200:503;return new Response(JSON.stringify({name:t.name,status:t.status}),{headers:{"Content-Type":`application/json`},status:n})},path:t,type:`dynamic`}),te=(e,t=O)=>({handler:()=>{let t=e(),n=t.status===`running`?200:503;return new Response(JSON.stringify(t),{headers:{"Content-Type":`application/json`},status:n})},path:t,protected:!0,type:`dynamic`}),k=`/info`,A=(e,t=k)=>({handler:()=>new Response(JSON.stringify(e()),{headers:{"Content-Type":`application/json`},status:200}),path:t,protected:!0,type:`dynamic`}),j=()=>n(globalThis.process),M=e=>n(globalThis[e]),N=()=>M(`navigator`).flatMap(e=>n(e.userAgent)),P=()=>N().contains(`Cloudflare-Workers`)||M(`Deno`).isEmpty===!1||M(`EdgeRuntime`).isEmpty===!1,F=()=>!P()&&j().flatMap(e=>n(e.versions?.node)).isEmpty===!1,I=e=>j().flatMap(t=>n(t.env?.[e])).filter(e=>e.length>0),L=()=>({branch:I(`SOMAMCP_BUILD_BRANCH`).orUndefined(),commit:I(`SOMAMCP_BUILD_COMMIT`).orUndefined(),date:I(`SOMAMCP_BUILD_DATE`).orUndefined(),environment:I(`SOMAMCP_ENVIRONMENT`).orUndefined()}),ne=e=>{let t=L();return{branch:e?.branch??t.branch,commit:e?.commit??t.commit,date:e?.date??t.date,environment:e?.environment??t.environment}},R=`unknown`,z=()=>F()?{arch:j().flatMap(e=>n(e.arch)).orElse(R),nodeVersion:j().flatMap(e=>n(e.version)).orElse(R),platform:j().flatMap(e=>n(e.platform)).orElse(R),runtime:`node`}:{arch:R,nodeVersion:R,platform:N().orElse(R),runtime:`edge`},B=(e,t)=>{let i=r(n.none()),o=r(n.none()),l=r([]),u=r(`disconnected`),d=r(n.none()),f=e.name??e.id,p=()=>{let t=e.reconnectIntervalMs??5e3;d.set(n(setTimeout(()=>{m.connect()},t)))},m={get config(){return e},get info(){return{id:e.id,name:f,remoteTools:l.get().map(e=>({description:e.description,name:e.name})),status:u.get(),url:e.url}},get name(){return f},get status(){return u.get()},get tools(){return l.get()},async callTool(t,n={}){let r=i.get();if(r.isEmpty||u.get()!==`connected`)throw Error(`Gateway ${e.id} is not connected`);return r.orThrow(/* @__PURE__ */ Error(`Gateway ${e.id} is not connected`)).callTool({arguments:n,name:t})},async connect(){if(u.get()===`connected`)return;u.set(`connecting`);let r=Date.now();(await a.fromPromise((async()=>{let t=new c(new URL(e.url));o.set(n(t));let r=new s({name:`soma-gateway-${e.id}`,version:`1.0.0`});i.set(n(r)),await r.connect(t);let a=await r.listTools();l.set(a.tools)})())).fold(n=>{u.set(`error`),t.recordEvent({data:{id:e.id,url:e.url},error:n instanceof Error?n.message:String(n),name:f,timestamp:r,type:`gateway.error`}),e.reconnect!==!1&&p()},()=>{u.set(`connected`),t.recordEvent({data:{id:e.id,remoteToolCount:l.get().length,url:e.url},durationMs:Date.now()-r,name:f,timestamp:r,type:`gateway.connect`})})},async disconnect(){d.get().match({None:()=>void 0,Some:e=>clearTimeout(e)}),d.set(n.none());let r=i.get();r.isSome()&&await a.fromPromise(r.value.close()),i.set(n.none()),o.set(n.none()),l.set([]),u.set(`disconnected`),t.recordEvent({data:{id:e.id},name:f,timestamp:Date.now(),type:`gateway.disconnect`})}};return m},V=e=>{let n=r(t.empty()),i=()=>[...n.get()].map(([,e])=>e);return{get connectedCount(){return i().filter(e=>e.status===`connected`).length},get totalCount(){return n.get().size},add(t){let r=B(t,e);return n.set(n.get().add(o([t.id,r]))),r},async connectAll(){await Promise.allSettled(i().map(e=>e.connect()))},async disconnectAll(){await Promise.allSettled(i().map(e=>e.disconnect()))},get(e){return n.get().get(e)},getAll(){return i()},getInfoAll(){return i().map(e=>e.info)}}},H=e=>{if(e instanceof Error){let t=e.message.toLowerCase();if(t.includes(`validation`)||t.includes(`invalid`)||t.includes(`schema`))return`validation`;if(t.includes(`timeout`)||t.includes(`timed out`)||t.includes(`etimedout`))return`timeout`;if(t.includes(`gateway`)||t.includes(`not connected`)||t.includes(`econnrefused`)||t.includes(`connection refused`))return`gateway`;if(t.includes(`auth`)||t.includes(`unauthorized`)||t.includes(`forbidden`))return`auth`;if(t.includes(`not found`)||t.includes(`404`))return`not_found`}return typeof e==`object`&&e&&`name`in e&&e.name===`UserError`?`validation`:`internal`},U=(e,t)=>{switch(e){case`validation`:return[`Check the tool's parameter schema and provide valid input`];case`timeout`:return[`The operation timed out. Try again or reduce the scope of the request`];case`gateway`:return[`Gateway is ${t?.gatewayStatus??`unavailable`}. The remote service may be down`,`Try again later or check the remote service status`];case`auth`:return[`Authentication failed. Check credentials or permissions`];case`not_found`:return[`The requested resource was not found. Verify the identifier`];case`internal`:return[`An internal error occurred. The tool maintainer should investigate`]}},W=(e,t)=>{let n=H(e),r=e instanceof Error?e.message:String(e),i=U(n,t),a={...t?.gatewayStatus&&{gatewayStatus:t.gatewayStatus},...t?.toolName&&{toolName:t.toolName}};return{errorCategory:n,message:r,...Object.keys(a).length>0&&{relatedState:a},suggestions:i}},G=e=>a.fromPromise(e),K=(e,t)=>{let n=e.config.toolPrefix??`${e.config.id}_`;return e.tools.map(r=>({annotations:{openWorldHint:!0,readOnlyHint:!0},description:`[${e.name}] ${r.description??`Remote tool`}`,execute:async i=>{let a=Date.now(),o=`${n}${r.name}`;return(await G(e.callTool(r.name,i))).fold(n=>{let s=W(n,{gatewayStatus:e.status,toolName:r.name});return t?.recordEvent({data:{gateway:e.config.id,remoteTool:r.name},durationMs:Date.now()-a,error:s.message,errorCategory:s.errorCategory,errorStack:n.stack,input:i,name:o,timestamp:a,type:`tool.error`}),{content:[{text:JSON.stringify(s,null,2),type:`text`}],isError:!0}},n=>(t?.recordEvent({data:{gateway:e.config.id,remoteTool:r.name},durationMs:Date.now()-a,input:i,name:o,output:n,timestamp:a,type:`tool.execute`}),JSON.stringify(n,null,2)))},name:`${n}${r.name}`}))},q=(e,t=`info`)=>({annotations:{readOnlyHint:!0},description:`Returns identity and build information for this server (name, version, build commit, runtime, capability counts)`,execute:()=>Promise.resolve(JSON.stringify(e(),null,2)),name:t}),J=e=>({flush:()=>Promise.resolve(),recordEvent:t=>{let n={durationMs:t.durationMs,eventName:t.name,timestamp:new Date(t.timestamp).toISOString(),type:t.type,...t.sessionId&&{sessionId:t.sessionId},...t.requestId&&{requestId:t.requestId},...t.errorCategory&&{errorCategory:t.errorCategory},...t.data};t.error?e.withError(Error(t.error)).error(t.type,n):e.info(t.type,n)}}),Y={flush:()=>Promise.resolve(),recordEvent:()=>{}},re=(e,t)=>!t||t.length===0?e:Object.fromEntries(Object.entries(e).map(([e,n])=>[e,t.includes(e)?`[REDACTED]`:n])),ie=(e,t=1e4)=>{let n=typeof e==`string`?e:JSON.stringify(e);return n&&n.length>t?`${n.slice(0,t)}...[truncated, ${n.length} total chars]`:e},X=e=>a.fromPromise(e),Z=(e,t,n)=>({...e,execute:async(r,i)=>{let a=n?.captureLevel??`full`;if(a===`none`)return e.execute(r,i);let o=Date.now(),{requestId:s,sessionId:c}=i,l=a===`full`?re(r,n?.redactInputFields):void 0;return(await X(e.execute(r,i))).fold(n=>{let r=W(n,{toolName:e.name});return t.recordEvent({data:{name:e.name},durationMs:Date.now()-o,error:r.message,errorCategory:r.errorCategory,errorStack:n.stack,input:l,name:e.name,requestId:s,sessionId:c,timestamp:o,type:`tool.error`}),{content:[{text:JSON.stringify(r,null,2),type:`text`}],isError:!0}},r=>(t.recordEvent({data:{name:e.name},durationMs:Date.now()-o,input:l,name:e.name,output:a===`full`?ie(r,n?.maxOutputSize):void 0,requestId:s,sessionId:c,timestamp:o,type:`tool.execute`}),r))}}),ae=(e,t)=>({...e,load:async n=>{let r=Date.now();return(await X(e.load(n))).fold(n=>{let i=W(n);throw t.recordEvent({data:{name:e.name,uri:e.uri},durationMs:Date.now()-r,error:i.message,errorCategory:i.errorCategory,errorStack:n.stack,name:e.name,timestamp:r,type:`resource.error`}),n},n=>(t.recordEvent({data:{name:e.name,uri:e.uri},durationMs:Date.now()-r,name:e.name,timestamp:r,type:`resource.load`}),n))}}),oe=(e,t)=>({...e,load:async(n,r)=>{let i=Date.now();return(await X(e.load(n,r))).fold(n=>{let r=W(n);throw t.recordEvent({data:{name:e.name},durationMs:Date.now()-i,error:r.message,errorCategory:r.errorCategory,errorStack:n.stack,name:e.name,timestamp:i,type:`prompt.error`}),n},n=>(t.recordEvent({data:{name:e.name},durationMs:Date.now()-i,name:e.name,timestamp:i,type:`prompt.load`}),n))}}),se=e=>{let{artifacts:t,backend:i,backendOptions:a,build:o,enableDashboard:s,enableHealthEndpoint:c,enableInfoEndpoint:l,enableIntrospection:u,gateways:d,healthDetailPath:f,healthPath:p,infoPath:m,introspectionPrefix:h,logLayer:g,telemetry:v,...y}=e,b=y.name,x=y.version,S=ne(o),T=z(),E=h??``,D=v??(g?J(g):Y),O=i(y,a),k=V(D),j=[],M=[],N=[],P=r(0);d?.forEach(e=>k.add(e));let F=()=>{let e=P.get(),t=e>0?Date.now()-e:0;return{activeSessions:O.sessions.length,gateways:{connected:k.connectedCount,total:k.totalCount},name:b,startedAt:e,status:O.serverState,uptime:t}},I=()=>({prompts:N,resources:M,tools:j}),L=()=>({build:S,capabilities:{prompts:N.length,resources:M.length,tools:j.length},name:b,runtime:T,version:x}),R=e=>{j.some(t=>t.name===e.name)||j.push({description:e.description,name:e.name})},B=(e,t)=>{if(O.supportsRemoval===!1)return;let n=e.findIndex(e=>e.name===t);n>=0&&e.splice(n,1)};if(u!==!1){let e=q(()=>L(),`${E}info`);O.addTool(e),R(e)}let H=[...t??[]];c!==!1&&(H.push(ee(()=>F(),p)),H.push(te(()=>F(),f))),l!==!1&&H.push(A(()=>L(),m)),s!==!1&&H.push(w(()=>F(),()=>I(),()=>k.getInfoAll())),H.length>0&&C(O.getApp(),H,n(y.authenticate).orUndefined()),O.on(`connect`,({session:e})=>{D.recordEvent({data:{sessionId:e.sessionId},name:b,timestamp:Date.now(),type:`session.connect`})}),O.on(`disconnect`,({session:e})=>{D.recordEvent({data:{sessionId:e.sessionId},name:b,timestamp:Date.now(),type:`session.disconnect`})});let U=e=>{let t=`captureConfig`in e?e.captureConfig:void 0,n=Z(e,D,t);O.addTool(n),R(e)},W=e=>{let t=ae(e,D);O.addResource(t),M.push({description:e.description,name:e.name,uri:e.uri})},G=e=>{let t=oe(e,D);O.addPrompt(t),N.push({description:e.description,name:e.name})};return{get name(){return b},get serverState(){return O.serverState},get sessions(){return O.sessions},addPrompt:G,addPrompts:e=>{e.forEach(G)},addResource:W,addResources:e=>{e.forEach(W)},addResourceTemplate:(...e)=>{O.addResourceTemplate(...e)},addRoute:e=>{let t=O.getApp(),r=Array.isArray(e.method)?[...e.method]:[e.method];if(e.protected){let r=n(y.authenticate).orUndefined();t.use(e.path,_({authenticate:r,onUnauthorized:e.onUnauthorized}))}t.on(r,e.path,t=>e.handler(t))},addTool:U,addTools:e=>{e.forEach(e=>U(e))},fetch:async e=>O.fetch?O.fetch(e):O.getApp().fetch(e),getApp:()=>O.getApp(),getCapabilities:I,getGatewayManager:()=>k,getHealth:F,getInfo:L,removePrompt:e=>{O.removePrompt(e),B(N,e)},removeResource:e=>{O.removeResource(e),B(M,e)},removeTool:e=>{O.removeTool(e),B(j,e)},async start(e){P.set(Date.now()),D.recordEvent({name:b,timestamp:P.get(),type:`server.start`}),await O.start(e),await k.connectAll(),k.getAll().filter(e=>e.status===`connected`&&e.config.proxyTools!==!1).forEach(e=>{K(e,D).forEach(e=>{O.addTool(e),R(e)})})},async stop(){await k.disconnectAll(),await O.stop(),D.recordEvent({name:b,timestamp:Date.now(),type:`server.stop`}),D.flush&&await D.flush()}}};var ce=class extends Error{constructor(e){super(e),this.name=`UserError`}};const le={".avif":`image/avif`,".bmp":`image/bmp`,".gif":`image/gif`,".ico":`image/x-icon`,".jpeg":`image/jpeg`,".jpg":`image/jpeg`,".png":`image/png`,".svg":`image/svg+xml`,".tiff":`image/tiff`,".webp":`image/webp`},ue={".aac":`audio/aac`,".flac":`audio/flac`,".m4a":`audio/mp4`,".mp3":`audio/mpeg`,".ogg":`audio/ogg`,".opus":`audio/opus`,".wav":`audio/wav`,".weba":`audio/webm`},de=(e,t,n)=>t[p(new URL(e,`file:///`).pathname).toLowerCase()]??n,fe=async(e,t)=>{if(`url`in e){let n=await fetch(e.url);if(!n.ok)throw Error(`Failed to fetch ${t} from URL (${e.url}): ${n.status} ${n.statusText}`);return{data:Buffer.from(await n.arrayBuffer()),source:e.url}}return`path`in e?{data:await d(e.path),source:e.path}:{data:e.buffer}},pe=async e=>{let{data:t,source:n}=await fe(e,`image`),r=n?de(n,le,`image/png`):`image/png`;return{data:t.toString(`base64`),mimeType:r,type:`image`}},me=async e=>{let{data:t,source:n}=await fe(e,`audio`),r=n?de(n,ue,`audio/mpeg`):`audio/mpeg`;return{data:t.toString(`base64`),mimeType:r,type:`audio`}},he=e=>({flush:async()=>{await Promise.all(e.map(e=>e.flush?e.flush():Promise.resolve()))},recordEvent:t=>{e.forEach(e=>e.recordEvent(t))}}),ge=(e=`[soma]`)=>{let t=m({prefix:e,stream:`stderr`});return J(t)},_e=e=>{let t=r([]),i=r(n.none()),a=r(!1),o=async()=>{let n=t.getAndSet([]);if(n.length===0)return;a.get()||(await u(f(e.filePath),{recursive:!0}),a.set(!0));let r=`${n.map(e=>JSON.stringify(e)).join(`
58
- `)}\n`;await l(e.filePath,r)},s=()=>{i.get().isSome()||i.set(n(setTimeout(()=>{i.set(n.none()),o()},e.flushIntervalMs??1e3)))};return{flush:async()=>{i.get().match({None:()=>void 0,Some:e=>clearTimeout(e)}),i.set(n.none()),await o()},recordEvent:e=>{t.update(t=>[...t,e]),s()}}},ve=e=>m({prefix:`[${e}]`,stream:`stderr`}).withContext({cell:e}),ye=e=>({annotations:{readOnlyHint:!0},description:`Lists all tools, resources, and prompts registered on this server`,execute:()=>Promise.resolve(JSON.stringify(e(),null,2)),name:`soma_capabilities`}),be=e=>({annotations:{readOnlyHint:!0},description:`Lists all gateway connections to remote servers with their status and available tools`,execute:()=>Promise.resolve(JSON.stringify(e(),null,2)),name:`soma_connections`}),xe=e=>({annotations:{readOnlyHint:!0},description:`Returns health and status information for this server including uptime, sessions, and gateway status`,execute:()=>Promise.resolve(JSON.stringify(e(),null,2)),name:`soma_health`}),Q=[{description:`GitHub personal access token`,name:`github_pat`,pattern:/\b(?:ghp|gho|ghu|ghs|ghr|github_pat)_[A-Za-z0-9_]{20,}\b/g},{description:`AWS access key ID`,name:`aws_access_key`,pattern:/\bAKIA[0-9A-Z]{16}\b/g},{description:`AWS secret access key (heuristic)`,name:`aws_secret_key`,pattern:/(?<![A-Za-z0-9/+])[A-Za-z0-9/+]{40}(?![A-Za-z0-9/+])/g},{description:`Stripe secret/publishable key`,name:`stripe_key`,pattern:/\b(?:sk|pk|rk)_(?:live|test)_[A-Za-z0-9]{20,}\b/g},{description:`Slack token`,name:`slack_token`,pattern:/\bxox[abprs]-[A-Za-z0-9-]{10,}\b/g},{description:`JSON Web Token`,name:`jwt`,pattern:/\beyJ[A-Za-z0-9_-]{8,}\.eyJ[A-Za-z0-9_-]{8,}\.[A-Za-z0-9_-]+\b/g},{description:`OpenAI / Anthropic style API key`,name:`ai_api_key`,pattern:/\b(?:sk-ant-|sk-)[A-Za-z0-9_-]{20,}\b/g},{description:`Generic bearer-style secret prefix`,name:`bearer_authorization`,pattern:/\b[Bb]earer\s+[A-Za-z0-9._\-+/]{16,}\b/g},{description:`Email address`,name:`email`,pattern:/\b[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,}\b/g},{description:`Private IPv4 address (RFC1918)`,name:`private_ipv4`,pattern:/\b(?:10(?:\.\d{1,3}){3}|192\.168(?:\.\d{1,3}){2}|172\.(?:1[6-9]|2\d|3[01])(?:\.\d{1,3}){2})\b/g},{description:`URL pointing at internal/private hostname`,name:`internal_url`,pattern:/\bhttps?:\/\/(?:[a-z0-9-]+\.)*(?:internal|intranet|corp|local|lan)(?:[/:][^\s]*)?/gi}],$=(e,n=Q)=>{let{counts:r,text:i}=n.reduce((e,t)=>{let n=Array.from(e.text.matchAll(t.pattern)).length;return n===0?e:{counts:e.counts.add(o([t.name,n])),text:e.text.replace(t.pattern,t.replacement??`[REDACTED]`)}},{counts:t.empty(),text:e}),a=[...r].map(([e,t])=>({count:t,name:e}));return{matches:a,redacted:a.length>0,text:i}},Se=[`Submit a feedback report or issue to this server's maintainers.`,``,`IMPORTANT: Treat all input as potentially public.`,`- Do NOT include API keys, tokens, passwords, or credentials`,`- Do NOT include customer data, PII, or proprietary information`,`- Do NOT include internal URLs, hostnames, or system topology`,`- Describe the behavior and how to reproduce it, not the data involved`,``,`Common credential patterns are automatically redacted before submission,`,`but redaction is best-effort and is not a substitute for caution.`].join(`
59
- `),Ce=h.object({description:h.string().min(10,`Provide enough context to reproduce (>=10 chars)`).max(8e3).describe(`Detailed description. NO secrets, customer data, or internal URLs.`),severity:h.enum([`low`,`medium`,`high`]).optional().describe(`Severity hint (optional)`),title:h.string().min(5,`Title must be at least 5 characters`).max(200).describe(`Brief, public-safe summary`),type:h.enum([`bug`,`feature`,`feedback`,`question`]).default(`feedback`).describe(`What kind of report this is`)}),we=(e,r)=>{if(!e.redacted&&!r.redacted)return n.none();let i=[...[...e.matches,...r.matches].reduce((e,t)=>e.add(o([t.name,e.get(t.name).orElse(0)+t.count])),t.empty())].map(([e,t])=>`- ${e}: ${String(t)}`);return n([`⚠️ Automatic redactions applied:`,...i].join(`
60
- `))},Te=e=>e.filter(e=>Object.keys(e).length>0).map(e=>[`---`,`**Context (auto-collected):**`,"```json",JSON.stringify(e,null,2),"```"].join(`
61
- `)),Ee=(e,t,n,r,i)=>[`**Type:** ${t}`,...n.map(e=>`**Severity:** ${e}`).toArray(),``,e,...r.map(e=>[``,e]).toArray().flat(),...i.map(e=>[``,e]).toArray().flat()].join(`
62
- `),De=e=>{let t=e.redactionPatterns??Q;return{annotations:{destructiveHint:!1,readOnlyHint:!1},description:e.description??Se,execute:async r=>{let i=$(r.title,t),a=$(r.description,t),o=e.enrichment?n(await e.enrichment({severity:r.severity,type:r.type})):n.none(),s=we(i,a),c=Te(o),l=Ee(a.text,r.type,n(r.severity),s,c),u=[r.type,...r.severity?[`severity:${r.severity}`]:[],...e.extraLabels??[]],d=await e.provider.submit({body:l,labels:u,title:i.text});return d.success?JSON.stringify({id:d.id,provider:e.provider.name,redacted:s.isSome(),redactionDetails:[...i.matches,...a.matches],success:!0,url:d.url},null,2):JSON.stringify({error:d.error,redacted:s.isSome(),success:!1},null,2)},name:e.name??`report_feedback`,parameters:Ce}},Oe=e=>{let t=e.baseUrl??`https://api.github.com`,r=e.fetchImpl??fetch;return{name:`github`,submit:async o=>{let s=e.getToken();if(s.isEmpty)return{error:`No GitHub token configured`,success:!1};let c=s.orThrow(/* @__PURE__ */ Error(`unreachable`)),l=[...e.defaultLabels??[],...o.labels],u=JSON.stringify({body:o.body,labels:i(l).toArray(),title:o.title});return(await a.fromPromise(r(`${t}/repos/${e.repo}/issues`,{body:u,headers:{Accept:`application/vnd.github+json`,Authorization:`Bearer ${c}`,"Content-Type":`application/json`,"X-GitHub-Api-Version":`2022-11-28`},method:`POST`}))).foldAsync(e=>({error:e instanceof Error?e.message:`Unknown error submitting to GitHub`,success:!1}),async e=>{let t=await e.json().catch(()=>({}));return e.ok?{id:n(t.number).fold(()=>void 0,e=>String(e)),success:!0,url:t.html_url}:{error:`GitHub API ${String(e.status)}: ${t.message??e.statusText}`,success:!1}})}}},ke=e=>{let t=e.fetchImpl??fetch,n=e.transform??(e=>e);return{name:`webhook`,submit:async r=>(await a.fromPromise(t(e.url,{body:JSON.stringify(n(r)),headers:{"Content-Type":`application/json`,...e.headers??{}},method:`POST`}))).foldAsync(e=>({error:e instanceof Error?e.message:`Unknown error posting to webhook`,success:!1}),async e=>{if(!e.ok){let t=await e.text().catch(()=>``);return{error:`Webhook ${String(e.status)}: ${t||e.statusText}`,success:!1}}return{success:!0}})}},Ae=t=>se({...t,backend:t.backend??e});export{O as DEFAULT_HEALTH_DETAIL_PATH,D as DEFAULT_HEALTH_PATH,k as DEFAULT_INFO_PATH,Q as DEFAULT_REDACTION_PATTERNS,Y as NoopTelemetry,ce as UserError,me as audioContent,H as classifyError,_ as createAuthMiddleware,ye as createCapabilitiesTool,he as createCompositeTelemetry,be as createConnectionsTool,ge as createConsoleTelemetry,w as createDashboardArtifact,ve as createDefaultLogger,W as createEnrichedError,e as createFastMCPBackend,De as createFeedbackTool,B as createGateway,V as createGatewayManager,Oe as createGithubFeedback,ee as createHealthArtifact,te as createHealthDetailArtifact,xe as createHealthTool,A as createInfoArtifact,q as createInfoTool,_e as createJsonFileTelemetry,J as createLogLayerTelemetry,K as createProxiedTools,Ae as createServer,se as createServerCore,ke as createWebhookFeedback,S as getRequestHeader,z as getRuntimeInfo,pe as imageContent,L as readBuildInfoFromEnv,$ as redact,C as registerArtifacts,ne as resolveBuildInfo,oe as wrapPrompt,ae as wrapResource,Z as wrapTool};
1
+ import{n as e}from"./backend-DnC4_jJ7.js";import{A as t,B as n,C as r,D as i,E as a,F as o,H as s,I as c,L as l,M as u,N as d,O as f,P as p,R as m,S as h,T as g,V as _,_ as v,a as y,b,c as x,d as S,f as C,g as w,i as T,j as E,k as D,l as O,n as k,o as A,p as j,r as M,s as N,t as P,u as F,v as I,w as L,x as R,y as z,z as B}from"./src-C1b-whVb.js";export{o as DEFAULT_HEALTH_DETAIL_PATH,c as DEFAULT_HEALTH_PATH,d as DEFAULT_INFO_PATH,y as DEFAULT_REDACTION_PATTERNS,h as NoopTelemetry,v as UserError,j as audioContent,a as classifyError,s as createAuthMiddleware,O as createCapabilitiesTool,C as createCompositeTelemetry,x as createConnectionsTool,S as createConsoleTelemetry,B as createDashboardArtifact,F as createDefaultLogger,i as createEnrichedError,T as createFeedbackTool,D as createGateway,f as createGatewayManager,M as createGithubFeedback,l as createHealthArtifact,m as createHealthDetailArtifact,N as createHealthTool,p as createInfoArtifact,L as createInfoTool,r as createLogLayerTelemetry,g as createProxiedTools,e as createSdkBackend,P as createServer,I as createServerCore,k as createWebhookFeedback,_ as getRequestHeader,t as getRuntimeInfo,w as imageContent,E as readBuildInfoFromEnv,A as redact,n as registerArtifacts,u as resolveBuildInfo,z as wrapPrompt,b as wrapResource,R as wrapTool};
@@ -0,0 +1,28 @@
1
+ import { A as SessionAuth, C as ResourceContent, D as ResourceTemplateArgument, E as ResourceTemplate, M as Tool, N as ToolAnnotations, O as SchemaParams, P as UserError, S as Resource, T as ResourceResult, _ as InferSchemaOutput, a as BackendFactory, b as PromptArgument, c as Logger, d as AudioContent, f as Completion, g as ImageContent, h as Context, j as TextContent, k as ServerStatus, l as ServerConfig, m as ContentResult, n as createSdkBackend, o as BackendSession, p as Content, r as BackendAdapter, s as HttpStreamConfig, t as SdkBackendOptions, u as TransportConfig, v as Progress, w as ResourceLink, x as PromptResult, y as Prompt } from "../index-ololl9dI.js";
2
+ import { $ as ServerHealth, A as DEFAULT_HEALTH_DETAIL_PATH, At as DynamicArtifact, B as NoopTelemetry, C as redact, Ct as RuntimeInfo, D as createCapabilitiesTool, Dt as ArtifactAuthenticate, E as createConnectionsTool, Et as resolveBuildInfo, F as registerArtifacts, G as createConsoleTelemetry, H as EnrichedErrorResponse, I as createDefaultLogger, K as createCompositeTelemetry, L as wrapPrompt, M as createHealthArtifact, N as createHealthDetailArtifact, O as DEFAULT_INFO_PATH, Ot as ArtifactConfig, P as createDashboardArtifact, Q as ServerCapabilities, R as wrapResource, S as RedactionResult, St as BuildInfo, T as createHealthTool, Tt as readBuildInfoFromEnv, U as classifyError, V as createLogLayerTelemetry, W as createEnrichedError, X as createServerCore, Z as CapabilitiesSummary, _ as FeedbackSubmitSuccess, _t as GatewayConfig, a as createGateway, at as RouteMethod, b as DEFAULT_REDACTION_PATTERNS, bt as GatewayManagerInstance, c as GithubFeedbackOptions, ct as Authenticate, d as FeedbackToolOptions, dt as CaptureLevel, et as ServerInfo, f as createFeedbackTool, ft as ErrorCategory, g as FeedbackSubmitResult, gt as ToolCaptureConfig, h as FeedbackSubmitFailure, ht as TelemetryEventType, i as createGatewayManager, it as RouteConfig, j as DEFAULT_HEALTH_PATH, jt as StaticArtifact, k as createInfoArtifact, kt as DirectoryArtifact, l as createGithubFeedback, lt as OnUnauthorized, m as FeedbackSeverity, mt as TelemetryEvent, nt as SomaServerOptions, o as WebhookFeedbackOptions, ot as getRequestHeader, p as FeedbackProvider, pt as TelemetryCollector, q as ContentInput, r as createProxiedTools, rt as ToolOptions, s as createWebhookFeedback, st as AuthMiddlewareConfig, t as DirectLogger, tt as SomaServerInstance, u as FeedbackEnrichmentContext, ut as createAuthMiddleware, v as FeedbackType, vt as GatewayInfo, w as createInfoTool, wt as getRuntimeInfo, x as RedactionPattern, xt as GatewayStatus, y as NormalizedFeedback, yt as GatewayInstance, z as wrapTool } from "../index-aVpYZPUq.js";
3
+ /**
4
+ * The SDK backend with Node transports: `start({ transportType: "httpStream" })`
5
+ * listens on a port, and `start({ transportType: "stdio" })` or a bare `start()`
6
+ * serves stdio. `fetch()` works as it does in the main entry.
7
+ */
8
+ export declare const createNodeBackend: <T extends SessionAuth = SessionAuth>(config: ServerConfig<T>, backendOptions?: Record<string, unknown>) => BackendAdapter<T>;
9
+ /** The main entry's inputs, plus a file path, which only Node can read. */
10
+ type NodeContentInput = ContentInput | {
11
+ path: string;
12
+ };
13
+ /** Like the main entry's `imageContent`, and also reads `{ path }` from disk; the extension picks the MIME type. */
14
+ export declare const imageContent: (input: NodeContentInput) => Promise<ImageContent>;
15
+ /** Like the main entry's `audioContent`, and also reads `{ path }` from disk; the extension picks the MIME type. */
16
+ export declare const audioContent: (input: NodeContentInput) => Promise<AudioContent>;
17
+ type JsonFileTelemetryOptions = {
18
+ filePath: string;
19
+ flushIntervalMs?: number;
20
+ };
21
+ export declare const createJsonFileTelemetry: (options: JsonFileTelemetryOptions) => TelemetryCollector;
22
+ /**
23
+ * Create a server on the MCP SDK 2.x backend with Node transports. Identical to
24
+ * `somamcp`'s `createServer`, except that `start()` can listen on a port
25
+ * (`httpStream`) or serve stdio, which a bare `start()` does.
26
+ */
27
+ export declare const createServer: <T extends SessionAuth = SessionAuth>(options: SomaServerOptions<T>) => SomaServerInstance<T>;
28
+ export { type ArtifactAuthenticate, type ArtifactConfig, type AudioContent, type AuthMiddlewareConfig, type Authenticate, type BackendAdapter, type BackendFactory, type BackendSession, type BuildInfo, type CapabilitiesSummary, type CaptureLevel, type Completion, type Content, type ContentInput, type ContentResult, type Context, DEFAULT_HEALTH_DETAIL_PATH, DEFAULT_HEALTH_PATH, DEFAULT_INFO_PATH, DEFAULT_REDACTION_PATTERNS, type DirectLogger, type DirectoryArtifact, type DynamicArtifact, type EnrichedErrorResponse, type ErrorCategory, type FeedbackEnrichmentContext, type FeedbackProvider, type FeedbackSeverity, type FeedbackSubmitFailure, type FeedbackSubmitResult, type FeedbackSubmitSuccess, type FeedbackToolOptions, type FeedbackType, type GatewayConfig, type GatewayInfo, type GatewayInstance, type GatewayManagerInstance, type GatewayStatus, type GithubFeedbackOptions, type HttpStreamConfig, type ImageContent, type InferSchemaOutput, type JsonFileTelemetryOptions, type Logger, type NodeContentInput, NoopTelemetry, type NormalizedFeedback, type OnUnauthorized, type Progress, type Prompt, type PromptArgument, type PromptResult, type RedactionPattern, type RedactionResult, type Resource, type ResourceContent, type ResourceLink, type ResourceResult, type ResourceTemplate, type ResourceTemplateArgument, type RouteConfig, type RouteMethod, type RuntimeInfo, type SchemaParams, type SdkBackendOptions, type ServerCapabilities, type ServerConfig, type ServerHealth, type ServerInfo, type ServerStatus, type SessionAuth, type SomaServerInstance, type SomaServerOptions, type StaticArtifact, type TelemetryCollector, type TelemetryEvent, type TelemetryEventType, type TextContent, type Tool, type ToolAnnotations, type ToolCaptureConfig, type ToolOptions, type TransportConfig, UserError, type WebhookFeedbackOptions, classifyError, createAuthMiddleware, createCapabilitiesTool, createCompositeTelemetry, createConnectionsTool, createConsoleTelemetry, createDashboardArtifact, createDefaultLogger, createEnrichedError, createFeedbackTool, createGateway, createGatewayManager, createGithubFeedback, createHealthArtifact, createHealthDetailArtifact, createHealthTool, createInfoArtifact, createInfoTool, createLogLayerTelemetry, createProxiedTools, createSdkBackend, createServerCore, createWebhookFeedback, getRequestHeader, getRuntimeInfo, readBuildInfoFromEnv, redact, registerArtifacts, resolveBuildInfo, wrapPrompt, wrapResource, wrapTool };
@@ -0,0 +1,2 @@
1
+ import{n as e,t}from"../backend-DnC4_jJ7.js";import{A as n,B as r,C as i,D as a,E as o,F as s,H as c,I as l,L as u,M as d,N as f,O as p,P as m,R as h,S as g,T as _,V as v,_ as y,a as b,b as x,c as S,d as C,f as w,g as T,h as E,i as D,j as O,k,l as A,m as j,n as ee,o as M,p as N,r as P,s as F,u as I,v as L,w as R,x as z,y as B,z as V}from"../src-C1b-whVb.js";import{Option as H,Ref as U}from"functype";import{createServer as W}from"node:https";import{serve as G}from"@hono/node-server";import{serveStdio as K}from"@modelcontextprotocol/server/stdio";import{appendFile as q,mkdir as J,readFile as Y}from"node:fs/promises";import{dirname as X}from"node:path";const Z={http:(e,t)=>{let n=t.sslCert&&t.sslKey?W:void 0;return new Promise((r,i)=>{let a=G({fetch:e.fetch,hostname:t.host??`localhost`,port:t.port,...n?{createServer:n,serverOptions:{ca:t.sslCa,cert:t.sslCert,key:t.sslKey}}:{}},()=>r({close:()=>new Promise((e,t)=>{a.close(n=>n?t(n):e()),`closeAllConnections`in a&&a.closeAllConnections()})}));a.once(`error`,i)})},stdio:(e,t)=>Promise.resolve().then(()=>{let n=K(e,t?{transport:t}:{});return{close:()=>n.close()}})},Q=(e,n)=>t(H(Z),e,n),$=async e=>`path`in e?E(await Y(e.path),H(e.path)):T(e),te=async e=>`path`in e?j(await Y(e.path),H(e.path)):N(e),ne=e=>{let t=U([]),n=U(H.none()),r=U(!1),i=async()=>{let n=t.getAndSet([]);if(n.length===0)return;r.get()||(await J(X(e.filePath),{recursive:!0}),r.set(!0));let i=`${n.map(e=>JSON.stringify(e)).join(`
2
+ `)}\n`;await q(e.filePath,i)},a=()=>{n.get().isSome()||n.set(H(setTimeout(()=>{n.set(H.none()),i()},e.flushIntervalMs??1e3)))};return{flush:async()=>{n.get().match({None:()=>void 0,Some:e=>clearTimeout(e)}),n.set(H.none()),await i()},recordEvent:e=>{t.update(t=>[...t,e]),a()}}},re=e=>L({...e,backend:e.backend??Q});export{s as DEFAULT_HEALTH_DETAIL_PATH,l as DEFAULT_HEALTH_PATH,f as DEFAULT_INFO_PATH,b as DEFAULT_REDACTION_PATTERNS,g as NoopTelemetry,y as UserError,te as audioContent,o as classifyError,c as createAuthMiddleware,A as createCapabilitiesTool,w as createCompositeTelemetry,S as createConnectionsTool,C as createConsoleTelemetry,V as createDashboardArtifact,I as createDefaultLogger,a as createEnrichedError,D as createFeedbackTool,k as createGateway,p as createGatewayManager,P as createGithubFeedback,u as createHealthArtifact,h as createHealthDetailArtifact,F as createHealthTool,m as createInfoArtifact,R as createInfoTool,ne as createJsonFileTelemetry,i as createLogLayerTelemetry,Q as createNodeBackend,_ as createProxiedTools,e as createSdkBackend,re as createServer,L as createServerCore,ee as createWebhookFeedback,v as getRequestHeader,n as getRuntimeInfo,$ as imageContent,O as readBuildInfoFromEnv,M as redact,r as registerArtifacts,d as resolveBuildInfo,B as wrapPrompt,x as wrapResource,z as wrapTool};
@@ -0,0 +1,69 @@
1
+ import{n as e}from"./backend-DnC4_jJ7.js";import{Map as t,Option as n,Ref as r,Set as i,Try as a,Tuple as o}from"functype";import{Client as s,StreamableHTTPClientTransport as c}from"@modelcontextprotocol/client";import{createDirectConsoleLogger as l}from"functype-log";import{z as u}from"zod";const d=e=>e.json({error:`Unauthorized`},401),f=({authenticate:e,onUnauthorized:t})=>{let r=t??d;return async(t,i)=>{if(!e||(await a.fromPromise(Promise.resolve().then(()=>e(t.req.raw)).then(e=>n(e).orThrow(/* @__PURE__ */ Error(`Authentication required`))))).isFailure())return r(t);await i()}},p=(e,t)=>typeof e.get==`function`?n(e.get(t)):n.none(),m=(e,t,r)=>{let i=e;return n(i[t]??i[r]).map(e=>Array.isArray(e)?e[0]??``:e)},ee=(e,t,r)=>p(e,t).fold(()=>m(e,t,r),e=>n(e)),te=e=>n(e).flatMap(e=>n(e.headers)),h=(e,t)=>{let n=t.toLowerCase();return te(e).flatMap(e=>ee(e,n,t)).orUndefined()},ne=(e,t,n)=>{t.forEach(t=>{switch(t.protected&&e.use(t.path,f({authenticate:n})),t.type){case`static`:e.get(t.path,e=>e.body(t.content,{headers:{"Content-Type":t.contentType}}));break;case`dynamic`:e.get(t.path,e=>t.handler(e))}})},g=(e,t,n)=>({handler:()=>{let r=e(),i=t(),a=n(),o=_(r,i,a);return new Response(o,{headers:{"Content-Type":`text/html; charset=utf-8`}})},path:`/dashboard`,protected:!0,type:`dynamic`}),_=(e,t,n)=>`<!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>${e.name} - SomaMCP Dashboard</title>
7
+ <style>
8
+ * { margin: 0; padding: 0; box-sizing: border-box; }
9
+ body { font-family: system-ui, -apple-system, sans-serif; background: #0a0a0a; color: #e0e0e0; padding: 2rem; }
10
+ h1 { font-size: 1.5rem; margin-bottom: 1.5rem; color: #fff; }
11
+ h2 { font-size: 1.1rem; margin-bottom: 0.75rem; color: #a0a0a0; text-transform: uppercase; letter-spacing: 0.05em; }
12
+ .grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(300px, 1fr)); gap: 1.5rem; }
13
+ .card { background: #1a1a1a; border: 1px solid #2a2a2a; border-radius: 8px; padding: 1.25rem; }
14
+ .status { display: inline-block; padding: 2px 8px; border-radius: 4px; font-size: 0.85rem; font-weight: 600; }
15
+ .status.running { background: #0a3d0a; color: #4ade80; }
16
+ .status.stopped { background: #3d0a0a; color: #f87171; }
17
+ .status.error { background: #3d2a0a; color: #fbbf24; }
18
+ .status.connected { background: #0a3d0a; color: #4ade80; }
19
+ .status.disconnected { background: #3d0a0a; color: #f87171; }
20
+ .metric { margin: 0.5rem 0; }
21
+ .metric-label { color: #888; font-size: 0.85rem; }
22
+ .metric-value { color: #fff; font-weight: 500; }
23
+ ul { list-style: none; }
24
+ li { padding: 0.35rem 0; border-bottom: 1px solid #222; font-size: 0.9rem; }
25
+ li:last-child { border-bottom: none; }
26
+ .desc { color: #888; font-size: 0.8rem; }
27
+ </style>
28
+ </head>
29
+ <body>
30
+ <h1>${e.name}</h1>
31
+ <div class="grid">
32
+ <div class="card">
33
+ <h2>Health</h2>
34
+ <div class="metric"><span class="metric-label">Status:</span> <span class="status ${e.status}">${e.status}</span></div>
35
+ <div class="metric"><span class="metric-label">Uptime:</span> <span class="metric-value">${v(e.uptime)}</span></div>
36
+ <div class="metric"><span class="metric-label">Sessions:</span> <span class="metric-value">${e.activeSessions}</span></div>
37
+ <div class="metric"><span class="metric-label">Gateways:</span> <span class="metric-value">${e.gateways.connected}/${e.gateways.total}</span></div>
38
+ </div>
39
+ <div class="card">
40
+ <h2>Tools (${t.tools.length})</h2>
41
+ <ul>${t.tools.map(e=>`<li><strong>${e.name}</strong>${e.description?` <span class="desc">${e.description}</span>`:``}</li>`).join(``)}</ul>
42
+ </div>
43
+ <div class="card">
44
+ <h2>Resources (${t.resources.length})</h2>
45
+ <ul>${t.resources.map(e=>`<li><strong>${e.name}</strong> <span class="desc">${e.uri}</span></li>`).join(``)}</ul>
46
+ </div>
47
+ <div class="card">
48
+ <h2>Prompts (${t.prompts.length})</h2>
49
+ <ul>${t.prompts.map(e=>`<li><strong>${e.name}</strong>${e.description?` <span class="desc">${e.description}</span>`:``}</li>`).join(``)}</ul>
50
+ </div>
51
+ ${n.length>0?`<div class="card">
52
+ <h2>Gateways (${n.length})</h2>
53
+ <ul>${n.map(e=>`<li><strong>${e.name}</strong> <span class="status ${e.status}">${e.status}</span> <span class="desc">${e.url}</span></li>`).join(``)}</ul>
54
+ </div>`:``}
55
+ </div>
56
+ </body>
57
+ </html>`,v=e=>{let t=Math.floor(e/1e3),n=Math.floor(t/60),r=Math.floor(n/60),i=Math.floor(r/24);return i>0?`${String(i)}d ${String(r%24)}h`:r>0?`${String(r)}h ${String(n%60)}m`:n>0?`${String(n)}m ${String(t%60)}s`:`${String(t)}s`},y=`/health`,b=`/health/detail`,re=(e,t=y)=>({handler:()=>{let t=e(),n=t.status===`running`?200:503;return new Response(JSON.stringify({name:t.name,status:t.status}),{headers:{"Content-Type":`application/json`},status:n})},path:t,type:`dynamic`}),x=(e,t=b)=>({handler:()=>{let t=e(),n=t.status===`running`?200:503;return new Response(JSON.stringify(t),{headers:{"Content-Type":`application/json`},status:n})},path:t,protected:!0,type:`dynamic`}),S=`/info`,C=(e,t=S)=>({handler:()=>new Response(JSON.stringify(e()),{headers:{"Content-Type":`application/json`},status:200}),path:t,protected:!0,type:`dynamic`}),w=()=>n(globalThis.process),T=e=>n(globalThis[e]),E=()=>T(`navigator`).flatMap(e=>n(e.userAgent)),D=()=>E().contains(`Cloudflare-Workers`)||T(`Deno`).isEmpty===!1||T(`EdgeRuntime`).isEmpty===!1,O=()=>!D()&&w().flatMap(e=>n(e.versions?.node)).isEmpty===!1,k=e=>w().flatMap(t=>n(t.env?.[e])).filter(e=>e.length>0),A=()=>({branch:k(`SOMAMCP_BUILD_BRANCH`).orUndefined(),commit:k(`SOMAMCP_BUILD_COMMIT`).orUndefined(),date:k(`SOMAMCP_BUILD_DATE`).orUndefined(),environment:k(`SOMAMCP_ENVIRONMENT`).orUndefined()}),j=e=>{let t=A();return{branch:e?.branch??t.branch,commit:e?.commit??t.commit,date:e?.date??t.date,environment:e?.environment??t.environment}},M=`unknown`,N=()=>O()?{arch:w().flatMap(e=>n(e.arch)).orElse(M),nodeVersion:w().flatMap(e=>n(e.version)).orElse(M),platform:w().flatMap(e=>n(e.platform)).orElse(M),runtime:`node`}:{arch:M,nodeVersion:M,platform:E().orElse(M),runtime:`edge`},P=(e,t)=>{let i=r(n.none()),o=r(n.none()),l=r([]),u=r(`disconnected`),d=r(n.none()),f=e.name??e.id,p=()=>{let t=e.reconnectIntervalMs??5e3;d.set(n(setTimeout(()=>{m.connect()},t)))},m={get config(){return e},get info(){return{id:e.id,name:f,protocolVersion:i.get().flatMap(e=>n(e.getNegotiatedProtocolVersion())).orUndefined(),remoteTools:l.get().map(e=>({description:e.description,name:e.name})),status:u.get(),url:e.url}},get name(){return f},get status(){return u.get()},get tools(){return l.get()},async callTool(t,n={}){let r=i.get();if(r.isEmpty||u.get()!==`connected`)throw Error(`Gateway ${e.id} is not connected`);return r.orThrow(/* @__PURE__ */ Error(`Gateway ${e.id} is not connected`)).callTool({arguments:n,name:t})},async connect(){if(u.get()===`connected`)return;u.set(`connecting`);let r=Date.now();(await a.fromPromise((async()=>{let t=new c(new URL(e.url),{fetch:e.fetch});o.set(n(t));let r=new s({name:`soma-gateway-${e.id}`,version:`1.0.0`},{versionNegotiation:{mode:e.versionNegotiation??`auto`}});i.set(n(r)),await r.connect(t);let a=await r.listTools();l.set(a.tools)})())).fold(n=>{u.set(`error`),t.recordEvent({data:{id:e.id,url:e.url},error:n instanceof Error?n.message:String(n),name:f,timestamp:r,type:`gateway.error`}),e.reconnect!==!1&&p()},()=>{u.set(`connected`),t.recordEvent({data:{id:e.id,remoteToolCount:l.get().length,url:e.url},durationMs:Date.now()-r,name:f,timestamp:r,type:`gateway.connect`})})},async disconnect(){d.get().match({None:()=>void 0,Some:e=>clearTimeout(e)}),d.set(n.none());let r=i.get();r.isSome()&&await a.fromPromise(r.value.close()),i.set(n.none()),o.set(n.none()),l.set([]),u.set(`disconnected`),t.recordEvent({data:{id:e.id},name:f,timestamp:Date.now(),type:`gateway.disconnect`})}};return m},ie=e=>{let n=r(t.empty()),i=()=>[...n.get()].map(([,e])=>e);return{get connectedCount(){return i().filter(e=>e.status===`connected`).length},get totalCount(){return n.get().size},add(t){let r=P(t,e);return n.set(n.get().add(o([t.id,r]))),r},async connectAll(){await Promise.allSettled(i().map(e=>e.connect()))},async disconnectAll(){await Promise.allSettled(i().map(e=>e.disconnect()))},get(e){return n.get().get(e)},getAll(){return i()},getInfoAll(){return i().map(e=>e.info)}}},F=e=>{if(e instanceof Error){let t=e.message.toLowerCase();if(t.includes(`validation`)||t.includes(`invalid`)||t.includes(`schema`))return`validation`;if(t.includes(`timeout`)||t.includes(`timed out`)||t.includes(`etimedout`))return`timeout`;if(t.includes(`gateway`)||t.includes(`not connected`)||t.includes(`econnrefused`)||t.includes(`connection refused`))return`gateway`;if(t.includes(`auth`)||t.includes(`unauthorized`)||t.includes(`forbidden`))return`auth`;if(t.includes(`not found`)||t.includes(`404`))return`not_found`}return typeof e==`object`&&e&&`name`in e&&e.name===`UserError`?`validation`:`internal`},I=(e,t)=>{switch(e){case`validation`:return[`Check the tool's parameter schema and provide valid input`];case`timeout`:return[`The operation timed out. Try again or reduce the scope of the request`];case`gateway`:return[`Gateway is ${t?.gatewayStatus??`unavailable`}. The remote service may be down`,`Try again later or check the remote service status`];case`auth`:return[`Authentication failed. Check credentials or permissions`];case`not_found`:return[`The requested resource was not found. Verify the identifier`];case`internal`:return[`An internal error occurred. The tool maintainer should investigate`]}},L=(e,t)=>{let n=F(e),r=e instanceof Error?e.message:String(e),i=I(n,t),a={...t?.gatewayStatus&&{gatewayStatus:t.gatewayStatus},...t?.toolName&&{toolName:t.toolName}};return{errorCategory:n,message:r,...Object.keys(a).length>0&&{relatedState:a},suggestions:i}},R=e=>a.fromPromise(e),z=(e,t)=>{let n=e.config.toolPrefix??`${e.config.id}_`;return e.tools.map(r=>({annotations:{openWorldHint:!0,readOnlyHint:!0},description:`[${e.name}] ${r.description??`Remote tool`}`,execute:async i=>{let a=Date.now(),o=`${n}${r.name}`;return(await R(e.callTool(r.name,i))).fold(n=>{let s=L(n,{gatewayStatus:e.status,toolName:r.name});return t?.recordEvent({data:{gateway:e.config.id,remoteTool:r.name},durationMs:Date.now()-a,error:s.message,errorCategory:s.errorCategory,errorStack:n.stack,input:i,name:o,timestamp:a,type:`tool.error`}),{content:[{text:JSON.stringify(s,null,2),type:`text`}],isError:!0}},n=>(t?.recordEvent({data:{gateway:e.config.id,remoteTool:r.name},durationMs:Date.now()-a,input:i,name:o,output:n,timestamp:a,type:`tool.execute`}),JSON.stringify(n,null,2)))},name:`${n}${r.name}`}))},B=(e,t=`info`)=>({annotations:{readOnlyHint:!0},description:`Returns identity and build information for this server (name, version, build commit, runtime, capability counts)`,execute:()=>Promise.resolve(JSON.stringify(e(),null,2)),name:t}),V=e=>({flush:()=>Promise.resolve(),recordEvent:t=>{let n={durationMs:t.durationMs,eventName:t.name,timestamp:new Date(t.timestamp).toISOString(),type:t.type,...t.sessionId&&{sessionId:t.sessionId},...t.requestId&&{requestId:t.requestId},...t.errorCategory&&{errorCategory:t.errorCategory},...t.data};t.error?e.withError(Error(t.error)).error(t.type,n):e.info(t.type,n)}}),H={flush:()=>Promise.resolve(),recordEvent:()=>{}},U=(e,t)=>!t||t.length===0?e:Object.fromEntries(Object.entries(e).map(([e,n])=>[e,t.includes(e)?`[REDACTED]`:n])),W=(e,t=1e4)=>{let n=typeof e==`string`?e:JSON.stringify(e);return n&&n.length>t?`${n.slice(0,t)}...[truncated, ${n.length} total chars]`:e},G=e=>a.fromPromise(e),K=(e,t,n)=>({...e,execute:async(r,i)=>{let a=n?.captureLevel??`full`;if(a===`none`)return e.execute(r,i);let o=Date.now(),{requestId:s,sessionId:c}=i,l=a===`full`?U(r,n?.redactInputFields):void 0;return(await G(e.execute(r,i))).fold(n=>{let r=L(n,{toolName:e.name});return t.recordEvent({data:{name:e.name},durationMs:Date.now()-o,error:r.message,errorCategory:r.errorCategory,errorStack:n.stack,input:l,name:e.name,requestId:s,sessionId:c,timestamp:o,type:`tool.error`}),{content:[{text:JSON.stringify(r,null,2),type:`text`}],isError:!0}},r=>(t.recordEvent({data:{name:e.name},durationMs:Date.now()-o,input:l,name:e.name,output:a===`full`?W(r,n?.maxOutputSize):void 0,requestId:s,sessionId:c,timestamp:o,type:`tool.execute`}),r))}}),q=(e,t)=>({...e,load:async n=>{let r=Date.now();return(await G(e.load(n))).fold(n=>{let i=L(n);throw t.recordEvent({data:{name:e.name,uri:e.uri},durationMs:Date.now()-r,error:i.message,errorCategory:i.errorCategory,errorStack:n.stack,name:e.name,timestamp:r,type:`resource.error`}),n},n=>(t.recordEvent({data:{name:e.name,uri:e.uri},durationMs:Date.now()-r,name:e.name,timestamp:r,type:`resource.load`}),n))}}),J=(e,t)=>({...e,load:async(n,r)=>{let i=Date.now();return(await G(e.load(n,r))).fold(n=>{let r=L(n);throw t.recordEvent({data:{name:e.name},durationMs:Date.now()-i,error:r.message,errorCategory:r.errorCategory,errorStack:n.stack,name:e.name,timestamp:i,type:`prompt.error`}),n},n=>(t.recordEvent({data:{name:e.name},durationMs:Date.now()-i,name:e.name,timestamp:i,type:`prompt.load`}),n))}}),Y=e=>{let{artifacts:t,backend:i,backendOptions:a,build:o,enableDashboard:s,enableHealthEndpoint:c,enableInfoEndpoint:l,enableIntrospection:u,gateways:d,healthDetailPath:p,healthPath:m,infoPath:ee,introspectionPrefix:te,logLayer:h,telemetry:_,...v}=e,y=v.name,b=v.version,S=j(o),w=N(),T=te??``,E=_??(h?V(h):H),D=i(v,a),O=ie(E),k=[],A=[],M=[],P=r(0);d?.forEach(e=>O.add(e));let F=()=>{let e=P.get(),t=e>0?Date.now()-e:0;return{activeSessions:D.sessions.length,gateways:{connected:O.connectedCount,total:O.totalCount},name:y,startedAt:e,status:D.serverState,uptime:t}},I=()=>({prompts:M,resources:A,tools:k}),L=()=>({build:S,capabilities:{prompts:M.length,resources:A.length,tools:k.length},name:y,runtime:w,version:b}),R=e=>{k.some(t=>t.name===e.name)||k.push({description:e.description,name:e.name})},U=(e,t)=>{if(D.supportsRemoval===!1)return;let n=e.findIndex(e=>e.name===t);n>=0&&e.splice(n,1)};if(u!==!1){let e=B(()=>L(),`${T}info`);D.addTool(e),R(e)}let W=[...t??[]];c!==!1&&(W.push(re(()=>F(),m)),W.push(x(()=>F(),p))),l!==!1&&W.push(C(()=>L(),ee)),s!==!1&&W.push(g(()=>F(),()=>I(),()=>O.getInfoAll())),W.length>0&&ne(D.getApp(),W,n(v.authenticate).orUndefined()),D.on(`connect`,({session:e})=>{E.recordEvent({data:{sessionId:e.sessionId},name:y,timestamp:Date.now(),type:`session.connect`})}),D.on(`disconnect`,({session:e})=>{E.recordEvent({data:{sessionId:e.sessionId},name:y,timestamp:Date.now(),type:`session.disconnect`})});let G=e=>{let t=`captureConfig`in e?e.captureConfig:void 0,n=K(e,E,t);D.addTool(n),R(e)},Y=e=>{let t=q(e,E);D.addResource(t),A.push({description:e.description,name:e.name,uri:e.uri})},X=e=>{let t=J(e,E);D.addPrompt(t),M.push({description:e.description,name:e.name})};return{get name(){return y},get serverState(){return D.serverState},get sessions(){return D.sessions},addPrompt:X,addPrompts:e=>{e.forEach(X)},addResource:Y,addResources:e=>{e.forEach(Y)},addResourceTemplate:e=>{D.addResourceTemplate(e)},addRoute:e=>{let t=D.getApp(),r=Array.isArray(e.method)?[...e.method]:[e.method];if(e.protected){let r=n(v.authenticate).orUndefined();t.use(e.path,f({authenticate:r,onUnauthorized:e.onUnauthorized}))}t.on(r,e.path,t=>e.handler(t))},addTool:G,addTools:e=>{e.forEach(e=>G(e))},fetch:async e=>D.fetch?D.fetch(e):D.getApp().fetch(e),getApp:()=>D.getApp(),getCapabilities:I,getGatewayManager:()=>O,getHealth:F,getInfo:L,removePrompt:e=>{D.removePrompt(e),U(M,e)},removeResource:e=>{D.removeResource(e),U(A,e)},removeTool:e=>{D.removeTool(e),U(k,e)},async start(e){P.set(Date.now()),E.recordEvent({name:y,timestamp:P.get(),type:`server.start`}),await D.start(e),await O.connectAll(),O.getAll().filter(e=>e.status===`connected`&&e.config.proxyTools!==!1).forEach(e=>{z(e,E).forEach(e=>{D.addTool(e),R(e)})})},async stop(){await O.disconnectAll(),await D.stop(),E.recordEvent({name:y,timestamp:Date.now(),type:`server.stop`}),E.flush&&await E.flush()}}};var X=class extends Error{constructor(e){super(e),this.name=`UserError`}};const ae={".avif":`image/avif`,".bmp":`image/bmp`,".gif":`image/gif`,".ico":`image/x-icon`,".jpeg":`image/jpeg`,".jpg":`image/jpeg`,".png":`image/png`,".svg":`image/svg+xml`,".tiff":`image/tiff`,".webp":`image/webp`},oe={".aac":`audio/aac`,".flac":`audio/flac`,".m4a":`audio/mp4`,".mp3":`audio/mpeg`,".ogg":`audio/ogg`,".opus":`audio/opus`,".wav":`audio/wav`,".weba":`audio/webm`},se=e=>{let t=new URL(e,`file:///`).pathname,n=t.lastIndexOf(`.`);return n>t.lastIndexOf(`/`)+1?t.slice(n).toLowerCase():``},Z=32768,ce=e=>btoa(Array.from({length:Math.ceil(e.length/Z)},(t,n)=>String.fromCharCode(...e.subarray(n*Z,(n+1)*Z))).join(``)),le=(e,t,r)=>e.flatMap(e=>n(t[se(e)])).orElse(r),ue=(e,t)=>({data:ce(e),mimeType:le(t,ae,`image/png`),type:`image`}),de=(e,t)=>({data:ce(e),mimeType:le(t,oe,`audio/mpeg`),type:`audio`}),fe=async(e,t)=>{if(`buffer`in e)return{data:e.buffer,source:n.none()};let r=await fetch(e.url);if(!r.ok)throw Error(`Failed to fetch ${t} from URL (${e.url}): ${r.status} ${r.statusText}`);return{data:new Uint8Array(await r.arrayBuffer()),source:n(e.url)}},pe=async e=>{let{data:t,source:n}=await fe(e,`image`);return ue(t,n)},me=async e=>{let{data:t,source:n}=await fe(e,`audio`);return de(t,n)},he=e=>({flush:async()=>{await Promise.all(e.map(e=>e.flush?e.flush():Promise.resolve()))},recordEvent:t=>{e.forEach(e=>e.recordEvent(t))}}),ge=(e=`[soma]`)=>{let t=l({prefix:e,stream:`stderr`});return V(t)},_e=e=>l({prefix:`[${e}]`,stream:`stderr`}).withContext({cell:e}),ve=e=>({annotations:{readOnlyHint:!0},description:`Lists all tools, resources, and prompts registered on this server`,execute:()=>Promise.resolve(JSON.stringify(e(),null,2)),name:`soma_capabilities`}),ye=e=>({annotations:{readOnlyHint:!0},description:`Lists all gateway connections to remote servers with their status and available tools`,execute:()=>Promise.resolve(JSON.stringify(e(),null,2)),name:`soma_connections`}),be=e=>({annotations:{readOnlyHint:!0},description:`Returns health and status information for this server including uptime, sessions, and gateway status`,execute:()=>Promise.resolve(JSON.stringify(e(),null,2)),name:`soma_health`}),Q=[{description:`GitHub personal access token`,name:`github_pat`,pattern:/\b(?:ghp|gho|ghu|ghs|ghr|github_pat)_[A-Za-z0-9_]{20,}\b/g},{description:`AWS access key ID`,name:`aws_access_key`,pattern:/\bAKIA[0-9A-Z]{16}\b/g},{description:`AWS secret access key (heuristic)`,name:`aws_secret_key`,pattern:/(?<![A-Za-z0-9/+])[A-Za-z0-9/+]{40}(?![A-Za-z0-9/+])/g},{description:`Stripe secret/publishable key`,name:`stripe_key`,pattern:/\b(?:sk|pk|rk)_(?:live|test)_[A-Za-z0-9]{20,}\b/g},{description:`Slack token`,name:`slack_token`,pattern:/\bxox[abprs]-[A-Za-z0-9-]{10,}\b/g},{description:`JSON Web Token`,name:`jwt`,pattern:/\beyJ[A-Za-z0-9_-]{8,}\.eyJ[A-Za-z0-9_-]{8,}\.[A-Za-z0-9_-]+\b/g},{description:`OpenAI / Anthropic style API key`,name:`ai_api_key`,pattern:/\b(?:sk-ant-|sk-)[A-Za-z0-9_-]{20,}\b/g},{description:`Generic bearer-style secret prefix`,name:`bearer_authorization`,pattern:/\b[Bb]earer\s+[A-Za-z0-9._\-+/]{16,}\b/g},{description:`Email address`,name:`email`,pattern:/\b[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,}\b/g},{description:`Private IPv4 address (RFC1918)`,name:`private_ipv4`,pattern:/\b(?:10(?:\.\d{1,3}){3}|192\.168(?:\.\d{1,3}){2}|172\.(?:1[6-9]|2\d|3[01])(?:\.\d{1,3}){2})\b/g},{description:`URL pointing at internal/private hostname`,name:`internal_url`,pattern:/\bhttps?:\/\/(?:[a-z0-9-]+\.)*(?:internal|intranet|corp|local|lan)(?:[/:][^\s]*)?/gi}],$=(e,n=Q)=>{let{counts:r,text:i}=n.reduce((e,t)=>{let n=Array.from(e.text.matchAll(t.pattern)).length;return n===0?e:{counts:e.counts.add(o([t.name,n])),text:e.text.replace(t.pattern,t.replacement??`[REDACTED]`)}},{counts:t.empty(),text:e}),a=[...r].map(([e,t])=>({count:t,name:e}));return{matches:a,redacted:a.length>0,text:i}},xe=u.object({description:u.string().min(10,`Provide enough context to reproduce (>=10 chars)`).max(8e3).describe(`Detailed description. NO secrets, customer data, or internal URLs.`),severity:u.enum([`low`,`medium`,`high`]).optional().describe(`Severity hint (optional)`),title:u.string().min(5,`Title must be at least 5 characters`).max(200).describe(`Brief, public-safe summary`),type:u.enum([`bug`,`feature`,`feedback`,`question`]).default(`feedback`).describe(`What kind of report this is`)}),Se=(e,r)=>{if(!e.redacted&&!r.redacted)return n.none();let i=[...[...e.matches,...r.matches].reduce((e,t)=>e.add(o([t.name,e.get(t.name).orElse(0)+t.count])),t.empty())].map(([e,t])=>`- ${e}: ${String(t)}`);return n([`⚠️ Automatic redactions applied:`,...i].join(`
58
+ `))},Ce=e=>e.filter(e=>Object.keys(e).length>0).map(e=>[`---`,`**Context (auto-collected):**`,"```json",JSON.stringify(e,null,2),"```"].join(`
59
+ `)),we=(e,t,n,r,i)=>[`**Type:** ${t}`,...n.map(e=>`**Severity:** ${e}`).toArray(),``,e,...r.map(e=>[``,e]).toArray().flat(),...i.map(e=>[``,e]).toArray().flat()].join(`
60
+ `),Te=e=>{let t=e.redactionPatterns??Q;return{annotations:{destructiveHint:!1,readOnlyHint:!1},description:e.description??`Submit a feedback report or issue to this server's maintainers.
61
+
62
+ IMPORTANT: Treat all input as potentially public.
63
+ - Do NOT include API keys, tokens, passwords, or credentials
64
+ - Do NOT include customer data, PII, or proprietary information
65
+ - Do NOT include internal URLs, hostnames, or system topology
66
+ - Describe the behavior and how to reproduce it, not the data involved
67
+
68
+ Common credential patterns are automatically redacted before submission,
69
+ but redaction is best-effort and is not a substitute for caution.`,execute:async r=>{let i=$(r.title,t),a=$(r.description,t),o=e.enrichment?n(await e.enrichment({severity:r.severity,type:r.type})):n.none(),s=Se(i,a),c=Ce(o),l=we(a.text,r.type,n(r.severity),s,c),u=[r.type,...r.severity?[`severity:${r.severity}`]:[],...e.extraLabels??[]],d=await e.provider.submit({body:l,labels:u,title:i.text});return d.success?JSON.stringify({id:d.id,provider:e.provider.name,redacted:s.isSome(),redactionDetails:[...i.matches,...a.matches],success:!0,url:d.url},null,2):JSON.stringify({error:d.error,redacted:s.isSome(),success:!1},null,2)},name:e.name??`report_feedback`,parameters:xe}},Ee=e=>{let t=e.baseUrl??`https://api.github.com`,r=e.fetchImpl??fetch;return{name:`github`,submit:async o=>{let s=e.getToken();if(s.isEmpty)return{error:`No GitHub token configured`,success:!1};let c=s.orThrow(/* @__PURE__ */ Error(`unreachable`)),l=[...e.defaultLabels??[],...o.labels],u=JSON.stringify({body:o.body,labels:i(l).toArray(),title:o.title});return(await a.fromPromise(r(`${t}/repos/${e.repo}/issues`,{body:u,headers:{Accept:`application/vnd.github+json`,Authorization:`Bearer ${c}`,"Content-Type":`application/json`,"X-GitHub-Api-Version":`2022-11-28`},method:`POST`}))).foldAsync(e=>({error:e instanceof Error?e.message:`Unknown error submitting to GitHub`,success:!1}),async e=>{let t=await e.json().catch(()=>({}));return e.ok?{id:n(t.number).fold(()=>void 0,e=>String(e)),success:!0,url:t.html_url}:{error:`GitHub API ${String(e.status)}: ${t.message??e.statusText}`,success:!1}})}}},De=e=>{let t=e.fetchImpl??fetch,n=e.transform??(e=>e);return{name:`webhook`,submit:async r=>(await a.fromPromise(t(e.url,{body:JSON.stringify(n(r)),headers:{"Content-Type":`application/json`,...e.headers??{}},method:`POST`}))).foldAsync(e=>({error:e instanceof Error?e.message:`Unknown error posting to webhook`,success:!1}),async e=>{if(!e.ok){let t=await e.text().catch(()=>``);return{error:`Webhook ${String(e.status)}: ${t||e.statusText}`,success:!1}}return{success:!0}})}},Oe=t=>Y({...t,backend:t.backend??e});export{N as A,ne as B,V as C,L as D,F as E,b as F,f as H,y as I,re as L,j as M,S as N,ie as O,C as P,x as R,H as S,z as T,h as V,X as _,Q as a,q as b,ye as c,ge as d,he as f,pe as g,ue as h,Te as i,A as j,P as k,ve as l,de as m,De as n,$ as o,me as p,Ee as r,be as s,Oe as t,_e as u,Y as v,B as w,K as x,J as y,g as z};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "somamcp",
3
- "version": "1.5.1",
3
+ "version": "2.0.0",
4
4
  "description": "Framework for building MCP servers with telemetry, introspection, and backend abstraction",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -12,6 +12,11 @@
12
12
  "import": "./dist/index.js",
13
13
  "default": "./dist/index.js"
14
14
  },
15
+ "./node": {
16
+ "types": "./dist/node/index.d.ts",
17
+ "import": "./dist/node/index.js",
18
+ "default": "./dist/node/index.js"
19
+ },
15
20
  "./edge": {
16
21
  "types": "./dist/edge.d.ts",
17
22
  "import": "./dist/edge.js",
@@ -19,7 +24,8 @@
19
24
  },
20
25
  "./backend": {
21
26
  "types": "./dist/backend/index.d.ts",
22
- "import": "./dist/backend/index.js"
27
+ "import": "./dist/backend/index.js",
28
+ "default": "./dist/backend/index.js"
23
29
  }
24
30
  },
25
31
  "files": [
@@ -35,11 +41,11 @@
35
41
  "test": "ts-builds test",
36
42
  "test:watch": "ts-builds test:watch",
37
43
  "test:coverage": "ts-builds test:coverage",
38
- "build": "ts-builds build && pnpm check:edge",
39
- "check:edge": "node scripts/check-edge-bundle.mjs",
44
+ "build": "ts-builds build && pnpm check:bundle",
40
45
  "dev": "ts-builds dev",
41
46
  "clean": "rimraf dist",
42
- "prepublishOnly": "pnpm validate && pnpm check:edge"
47
+ "prepublishOnly": "pnpm validate && pnpm check:bundle",
48
+ "check:bundle": "node scripts/check-worker-bundle.mjs"
43
49
  },
44
50
  "keywords": [
45
51
  "MCP",
@@ -60,21 +66,23 @@
60
66
  "homepage": "https://github.com/sapientsai/SomaMCP#readme",
61
67
  "dependencies": {
62
68
  "@hono/node-server": "^2.1.5",
63
- "@modelcontextprotocol/sdk": "^1.32.1",
69
+ "@modelcontextprotocol/client": "~2.3.1",
64
70
  "@modelcontextprotocol/server": "~2.3.1",
65
- "fastmcp": "^4.23.5",
66
- "functype": "^1.18.0",
67
- "functype-log": "^1.18.0",
71
+ "functype": "^1.19.0",
72
+ "functype-log": "^1.19.0",
68
73
  "hono": "^4.13.13",
69
74
  "zod": "^4.6.7"
70
75
  },
71
76
  "prettier": "ts-builds/prettier",
72
77
  "devDependencies": {
73
- "@modelcontextprotocol/client": "~2.3.1",
78
+ "@modelcontextprotocol/sdk": "^1.32.1",
74
79
  "@types/node": "^24.19.2",
80
+ "es-module-lexer": "^3.0.4",
81
+ "esbuild": "^0.28.2",
75
82
  "get-port-please": "^3.2.0",
76
- "ts-builds": "^4.0.0",
77
- "tsdown": "^0.23.1"
83
+ "miniflare": "^4.20260730.0",
84
+ "ts-builds": "^4.0.1",
85
+ "tsdown": "^0.23.2"
78
86
  },
79
- "packageManager": "pnpm@11.28.6+sha512.55d90390c3b39042f1505a2697789f0256c6d95bb28bdd7cb731af1887c9f2629ef581acb9f4e4ec772024c8203c2aed22a03299b97103fdcf32668e905d0024"
87
+ "packageManager": "pnpm@11.28.8+sha512.f6988a32046b9a3f78d7d7ee0747926bbe905ff9dff73bf19abb87bd7d1906a2ac90d93f73be32b5f9e14c2899569bcb0c9c5ec8a19e083f5d71bbab6dd9e0b8"
80
88
  }
@@ -1 +0,0 @@
1
- import{FastMCP as e,ServerState as t}from"fastmcp";import{ProtocolError as n,ProtocolErrorCode as r,ResourceNotFoundError as i,Server as a,UriTemplate as o,createMcpHandler as s}from"@modelcontextprotocol/server";import{List as c,Map as l,Option as u,Ref as d,Try as f,Tuple as p}from"functype";import{Hono as m}from"hono";import{cors as h}from"hono/cors";const g=e=>{switch(e){case t.Running:return`running`;case t.Error:return`error`;default:return`stopped`}},_=(t,n)=>{let r={...n,authenticate:t.authenticate,instructions:t.instructions,logger:t.logger,name:t.name,...t.streamKeepalive?{streamKeepalive:t.streamKeepalive}:{},version:t.version},i=new e(r);return{get serverState(){return g(i.serverState)},get sessions(){return i.sessions.map(e=>({sessionId:e.sessionId}))},addPrompt:e=>{i.addPrompt(e)},addResource:e=>{i.addResource(e)},addResourceTemplate:(...e)=>{i.addResourceTemplate(...e)},addTool:e=>{i.addTool(e)},fetch:async e=>i.getApp().fetch(e),getApp:()=>i.getApp(),on:(e,t)=>{i.on(e,t)},removePrompt:e=>i.removePrompt(e),removeResource:e=>i.removeResource(e),removeTool:e=>i.removeTool(e),start:async e=>{e?e.transportType===`httpStream`?await i.start({httpStream:e.httpStream,transportType:`httpStream`}):await i.start({transportType:`stdio`}):await i.start()},stop:()=>i.stop()}},v={properties:{},type:`object`},y=e=>u(e[`~standard`].jsonSchema).isSome(),b=e=>e.flatMap(e=>u(e[`~standard`].jsonSchema)).map(e=>e.input({target:`draft-2020-12`})).orElse(v),x=e=>typeof e==`string`?{content:[{text:e,type:`text`}]}:e===void 0?{content:[]}:`content`in e?e:{content:[e]},S=e=>({content:[{text:e,type:`text`}],isError:!0}),C=(e,t)=>({contents:(Array.isArray(e)?e:[e]).map(e=>({mimeType:e.mimeType??t.mimeType,uri:e.uri??t.uri,...`blob`in e?{blob:e.blob}:{text:e.text}}))}),w=e=>typeof e==`string`?{messages:[{content:{text:e,type:`text`},role:`user`}]}:e,T={values:[]},E=e=>({completion:e.orElse(T)}),D=(e,t)=>{let n=e.filter(e=>e.startsWith(t));return{total:n.length,values:n}},O=(e,t,n)=>{let r=t=>e.mcpReq.notify(t).catch(()=>{}),i=t=>(n,r)=>{e.mcpReq.log(t,r===void 0?n:{data:r,message:n}).catch(()=>{})},a=u(e.mcpReq._meta?.progressToken);return{client:{version:u(e.mcpReq.envelope?.[`io.modelcontextprotocol/clientInfo`]).map(e=>e.version).orUndefined()},log:{debug:i(`debug`),error:i(`error`),info:i(`info`),warn:i(`warning`)},reportProgress:async({progress:e,total:t})=>{await a.fold(async()=>{},n=>r({method:`notifications/progress`,params:{progress:e,progressToken:n,total:t}}))},requestId:String(e.mcpReq.id),session:t.orUndefined(),sessionId:e.sessionId,streamContent:async e=>{await r({method:`notifications/tool/streamContent`,params:{content:Array.isArray(e)?e:[e],toolName:n}})}}},k=e=>c([...e].map(([,e])=>e)),A=(...e)=>e.reduce((e,t)=>e.fold(()=>t(),()=>e),u.none()),j=e=>e instanceof Error?e.message:String(e),M=e=>`Tool '${e.name}' timed out after ${e.timeoutMs}ms. Consider increasing timeoutMs or optimizing the tool implementation.`,N=(e,t)=>u(e.timeoutMs).fold(()=>t,n=>{let r=d(u.none()),i=new Promise((t,i)=>{r.set(u(setTimeout(()=>i(Error(M(e))),n)))});return Promise.race([t,i]).finally(()=>r.get().forEach(clearTimeout))}),P=(e,t)=>u(e.parameters).fold(()=>Promise.resolve({value:t}),e=>Promise.resolve(e[`~standard`].validate(t))),F=e=>Object.fromEntries(Object.entries(e).map(([e,t])=>[e,Array.isArray(t)?t.join(`,`):t])),I=(e,t,s)=>{let l=s.orUndefined(),d=e=>s.fold(()=>!(t.authRequired&&e.canAccess),t=>!e.canAccess||e.canAccess(t)),p=t=>e.tools().get(t).filter(d),m=new a({name:t.name,version:t.version},{capabilities:{completions:{},logging:{},prompts:{},resources:{},tools:{}},instructions:t.instructions});m.setRequestHandler(`tools/list`,()=>({tools:k(e.tools()).filter(d).map(e=>({...e._meta?{_meta:e._meta}:{},...e.annotations?{annotations:e.annotations}:{},description:e.description,inputSchema:b(u(e.parameters)),name:e.name,...e.outputSchema?{outputSchema:b(u(e.outputSchema))}:{},...e.annotations?.title?{title:e.annotations.title}:{}})).toArray()}));let h=async(e,t,n)=>{let r=await P(e,t);return`issues`in r?S(`Invalid arguments for tool '${e.name}': ${r.issues.map(e=>e.message).join(`; `)}`):(await f.fromPromise(N(e,e.execute(r.value,n)))).fold(e=>S(j(e)),e=>x(e))},g=e=>p(e).orThrow(()=>new n(r.InvalidParams,`Unknown tool: ${e}`));m.setRequestHandler(`tools/call`,(e,t)=>{let{arguments:n,name:r}=e.params;return h(g(r),n??{},O(t,s,r))}),m.setRequestHandler(`resources/list`,()=>({resources:k(e.resources()).map(e=>({description:e.description,mimeType:e.mimeType,name:e.name,uri:e.uri})).toArray()})),m.setRequestHandler(`resources/templates/list`,()=>({resourceTemplates:k(e.templates()).map(e=>({description:e.description,mimeType:e.mimeType,name:e.name,uriTemplate:e.uriTemplate})).toArray()}));let _=t=>A(()=>k(e.resources()).find(e=>e.uri===t).map(e=>({load:()=>e.load(l),mimeType:e.mimeType})),()=>k(e.templates()).map(e=>({template:e,variables:u(new o(e.uriTemplate).match(t))})).find(e=>e.variables.isSome()).map(({template:e,variables:t})=>({load:()=>e.load(F(t.orElse({})),l),mimeType:e.mimeType}))),v=async e=>{let t=_(e).orThrow(()=>new i(e));return C(await t.load(),{mimeType:t.mimeType,uri:e})};m.setRequestHandler(`resources/read`,e=>v(e.params.uri)),m.setRequestHandler(`prompts/list`,()=>({prompts:k(e.prompts()).map(e=>({arguments:e.arguments?.map(e=>({description:e.description,name:e.name,required:e.required})),description:e.description,name:e.name})).toArray()}));let y=async(t,i)=>{let a=e.prompts().get(t).orThrow(()=>new n(r.InvalidParams,`Unknown prompt: ${t}`)),o=c(a.arguments??[]).filter(e=>e.required===!0&&!(e.name in i));if(!o.isEmpty){let e=o.map(e=>e.name).toArray().join(`, `);throw new n(r.InvalidParams,`Prompt '${t}' is missing required arguments: ${e}`)}return w(await a.load(i,l))};m.setRequestHandler(`prompts/get`,e=>y(e.params.name,e.params.arguments??{}));let T=(t,n,r)=>{let i=e.prompts().get(t),a=i.flatMap(e=>c(e.arguments??[]).find(e=>e.name===n));return A(()=>a.flatMap(e=>u(e.complete)).map(e=>()=>e(r,l)),()=>a.flatMap(e=>u(e.enum)).map(e=>()=>Promise.resolve(D(e,r))),()=>i.flatMap(e=>u(e.complete)).map(e=>()=>e(n,r,l)))},M=(t,n,r)=>{let i=k(e.templates()).find(e=>e.uriTemplate===t),a=i.flatMap(e=>c(e.arguments).find(e=>e.name===n));return A(()=>a.flatMap(e=>u(e.complete)).map(e=>()=>e(r,l)),()=>i.flatMap(e=>u(e.complete)).map(e=>()=>e(n,r,l)))};return m.setRequestHandler(`completion/complete`,async e=>{let{argument:t,ref:n}=e.params,r=n.type===`ref/prompt`?T(n.name,t.name,t.value):M(n.uri,t.name,t.value);return E(await r.foldAsync(()=>Promise.resolve(u.none()),async e=>u(await e())))}),m},L=`somamcp/session`,R=(e,t)=>{let n=u(e.logger),r=e=>n.forEach(t=>t.warn(`[somamcp/sdk] ${e}`)),i=d(l.empty()),a=d(l.empty()),o=d(l.empty()),c=d(l.empty()),g={prompts:()=>c.get(),resources:()=>a.get(),templates:()=>o.get(),tools:()=>i.get()},_=(e,t,n)=>{e.set(e.get().add(p([t,n])))},v=(e,t)=>{e.set(e.get().remove(t))},b={authRequired:e.authenticate!==void 0,instructions:e.instructions,name:e.name,version:e.version},x=u(e.streamKeepalive).filter(e=>e.enabled===!0).map(e=>e.intervalMs??2e4),S=x.fold(()=>`auto`,()=>`sse`),C=e=>s(({authInfo:e})=>I(g,b,u(e).flatMap(e=>u(e.extra?.[L]))),{keepAliveMs:x.orUndefined(),onerror:e=>n.forEach(t=>t.error(`[somamcp/sdk]`,e)),responseMode:e}),w=d(S),T=d(C(w.get())),E=d(u(t).flatMap(e=>u(e.mcpPath)).orElse(`/mcp`)),D=d(u.none()),O=d(u.none()),k=d(`running`),A=(e,t)=>t instanceof Response?t:e.json({error:`Unauthorized`},401),j=(e,t)=>Promise.resolve().then(()=>e(t)).then(e=>u(e).fold(()=>Promise.reject(/* @__PURE__ */ Error(`Authentication required`)),e=>Promise.resolve(e))),M=async t=>{let n=t.req.raw;return e.authenticate?j(e.authenticate,n).then(e=>T.get().fetch(n,{authInfo:{clientId:``,extra:{[L]:e},scopes:[],token:``}}),e=>A(t,e)):T.get().fetch(n)},N=new m;N.use(`*`,async(e,t)=>e.req.path===E.get()?D.get().fold(()=>M(e),async t=>await t(e,async()=>{e.res=await M(e)})??e.res):t());let P=e=>{u(e.endpoint).forEach(e=>E.set(e)),u(e.cors).filter(e=>e!==!1).forEach(e=>D.set(u(h(e===!0?{}:e)))),e.enableJsonResponse===!0&&(x.isSome()&&r(`enableJsonResponse disables streamKeepalive: JSON responses cannot carry keepalive frames.`),w.set(`json`),T.set(C(`json`))),e.stateless===!1&&r(`stateful sessions are not supported by the SDK backend; serving statelessly.`),e.eventStore!==void 0&&r(`eventStore is not supported by the SDK backend; ignoring.`)},F=async()=>{let{listenStdio:r}=await import(`./listen-ZkXBwyaw.js`),i=e.authenticate?(await f.fromPromise(j(e.authenticate,void 0))).fold(e=>(n.forEach(t=>t.error(`[somamcp/sdk] authentication failed for stdio transport:`,e.message)),u.none()),e=>u(e)):u.none();return r(()=>I(g,b,i),u(t).flatMap(e=>u(e.stdioTransport)).orUndefined())},R=async e=>{let{listenHttp:t}=await import(`./listen-ZkXBwyaw.js`);return P(e),t(N,e)};return{get serverState(){return k.get()},get sessions(){return[]},addPrompt:e=>{_(c,e.name,e)},addResource:e=>{_(a,e.name,e)},addResourceTemplate:(...e)=>{let t=e[0];_(o,t.name,t)},addTool:e=>{e.parameters&&!y(e.parameters)&&r(`tool "${e.name}": its schema has no ~standard.jsonSchema, so clients see an empty argument list. Use zod 4, ArkType, or Valibot with @valibot/to-json-schema.`),_(i,e.name,e)},fetch:async e=>N.fetch(e),getApp:()=>N,on:()=>{},removePrompt:e=>{v(c,e)},removeResource:e=>{v(a,e)},removeTool:e=>{v(i,e)},start:async e=>{if(O.get().isSome()){r(`start() called while already listening; ignoring. Call stop() first to restart.`);return}let t=await f.fromPromise(e?.transportType===`httpStream`?R(e.httpStream):F());t.isFailure()&&k.set(`error`),O.set(u(t.orThrow())),k.set(`running`)},stop:async()=>{await T.get().close(),await O.get().fold(async()=>{},e=>e.close()),O.set(u.none()),T.set(C(w.get())),k.set(`stopped`)}}};export{_ as n,R as t};
@@ -1 +0,0 @@
1
- const e=async(e,t)=>{let{serve:n}=await import(`@hono/node-server`),r=t.sslCert&&t.sslKey?(await import(`node:https`)).createServer:void 0;return new Promise((i,a)=>{let o=n({fetch:e.fetch,hostname:t.host??`localhost`,port:t.port,...r?{createServer:r,serverOptions:{ca:t.sslCa,cert:t.sslCert,key:t.sslKey}}:{}},()=>i({close:()=>new Promise((e,t)=>{o.close(n=>n?t(n):e()),`closeAllConnections`in o&&o.closeAllConnections()})}));o.once(`error`,a)})},t=async(e,t)=>{let{serveStdio:n}=await import(`@modelcontextprotocol/server/stdio`),r=n(e,t?{transport:t}:{});return{close:()=>r.close()}};export{e as listenHttp,t as listenStdio};