somamcp 1.2.0 → 1.3.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/README.md +6 -1
- package/dist/backend/index.d.ts +1 -1
- package/dist/edge.d.ts +9 -0
- package/dist/edge.js +7 -7
- package/dist/{index-BRNcy_4O.d.ts → index-pl2m5zH7.d.ts} +9 -0
- package/dist/index.d.ts +1 -1
- package/dist/index.js +8 -8
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -379,10 +379,13 @@ The root `somamcp` barrel exports `createJsonFileTelemetry`, `imageContent`, and
|
|
|
379
379
|
| Multi-result resources (`load()` returning array) | ✅ | ⚠️ first result only, warns |
|
|
380
380
|
| `reportProgress` / `streamContent` in tools | ✅ | ⚠️ inert no-ops |
|
|
381
381
|
| `removeTool` / `removeResource` / `removePrompt` | ✅ | ⚠️ warns, does nothing |
|
|
382
|
+
| `supportsRemoval` | `true` | `false` |
|
|
382
383
|
| `addResourceTemplate` | ✅ | ⚠️ warns, does nothing |
|
|
383
384
|
|
|
384
385
|
The ⚠️ rows are limits of `EdgeFastMCP`, which exposes no removal API, has no server→client channel, and treats resources as single-valued. They log through `config.logger` rather than failing, so passing a `logger` is recommended on edge.
|
|
385
386
|
|
|
387
|
+
**Capability counts.** `getInfo().capabilities` counts every tool actually served — including the built-in `info` tool and any proxied gateway tools. Because the edge backend reports `supportsRemoval: false`, a `removeTool` there leaves the count untouched: the tool is still being served, so it is still counted.
|
|
388
|
+
|
|
386
389
|
**Auth.** `EdgeFastMCPOptions` has no `authenticate` field, so somamcp gates the MCP endpoint itself with the same middleware that protects artifacts and routes. Behaviour matches Node: configure `authenticate`, and unauthenticated MCP calls get a 401.
|
|
387
390
|
|
|
388
391
|
**Tool errors.** `EdgeFastMCP` builds its response as `result: { content }` and discards `isError`, which would make a failed tool look successful. The edge adapter rethrows on `isError` so the client receives a JSON-RPC error instead.
|
|
@@ -393,7 +396,9 @@ The ⚠️ rows are limits of `EdgeFastMCP`, which exposes no removal API, has n
|
|
|
393
396
|
|
|
394
397
|
**Route precedence.** somamcp owns the outer Hono app and falls through to `EdgeFastMCP` for anything unmatched. This ordering is deliberate: `EdgeFastMCP` registers its own `/health` at construction, and Hono is first-match-wins, so mounting it first would shadow somamcp's health artifact.
|
|
395
398
|
|
|
396
|
-
**Runtime reporting.** `getInfo().runtime`
|
|
399
|
+
**Runtime reporting.** `getInfo().runtime` is `"edge"` on Cloudflare Workers, Deno, and Vercel Edge, and `"node"` otherwise. Edge runtimes are detected positively — `navigator.userAgent === "Cloudflare-Workers"`, or a `Deno` / `EdgeRuntime` global — rather than by the absence of `process`, because Workers with `nodejs_compat` supplies a working `process.versions.node`. On edge, `arch` / `nodeVersion` / `platform` report `"unknown"` or the user agent, rather than describing the compatibility shim.
|
|
400
|
+
|
|
401
|
+
Bun deliberately reports `"node"`: it is a Node-family server runtime with full Node APIs, so that is the accurate answer even though `somamcp/edge` runs there.
|
|
397
402
|
|
|
398
403
|
## Backend Abstraction
|
|
399
404
|
|
package/dist/backend/index.d.ts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { a as BackendSession, i as BackendFactory, n as BackendAdapter, r as BackendEvents, t as createFastMCPBackend } from "../index-
|
|
1
|
+
import { a as BackendSession, i as BackendFactory, n as BackendAdapter, r as BackendEvents, t as createFastMCPBackend } from "../index-pl2m5zH7.js";
|
|
2
2
|
export { type BackendAdapter, type BackendEvents, type BackendFactory, type BackendSession, createFastMCPBackend };
|
package/dist/edge.d.ts
CHANGED
|
@@ -253,6 +253,15 @@ type BackendAdapter<T extends SessionAuth = SessionAuth> = {
|
|
|
253
253
|
fetch?: (request: Request) => Promise<Response>;
|
|
254
254
|
getApp: () => Hono;
|
|
255
255
|
on: <E extends keyof BackendEvents<T>>(event: E, handler: BackendEvents<T>[E]) => void;
|
|
256
|
+
/**
|
|
257
|
+
* Whether `removeTool` / `removeResource` / `removePrompt` actually take
|
|
258
|
+
* effect. Defaults to true when absent.
|
|
259
|
+
*
|
|
260
|
+
* The edge backend sets this false: EdgeFastMCP exposes no removal API, so its
|
|
261
|
+
* `remove*` calls only warn. Capability counts must not shrink for a tool that
|
|
262
|
+
* is still being served.
|
|
263
|
+
*/
|
|
264
|
+
readonly supportsRemoval?: boolean;
|
|
256
265
|
removePrompt: (name: string) => void;
|
|
257
266
|
removeResource: (name: string) => void;
|
|
258
267
|
removeTool: (name: string) => void;
|
package/dist/edge.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import{EdgeFastMCP as e}from"fastmcp/edge";import{Map as t,Option as n,Ref as r,Set as i,Try as a,Tuple as o}from"functype";import{Hono as s}from"hono";import{Client as c}from"@modelcontextprotocol/sdk/client/index.js";import{StreamableHTTPClientTransport as l}from"@modelcontextprotocol/sdk/client/streamableHttp.js";import{createDirectConsoleLogger as u}from"functype-log";import{z as d}from"zod";const f=e=>e.json({error:`Unauthorized`},401),p=({authenticate:e,onUnauthorized:t})=>{let r=t??f;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()}},m=(e,t)=>typeof e.get==`function`?n(e.get(t)):n.none(),h=(e,t,r)=>{let i=e;return n(i[t]??i[r]).map(e=>Array.isArray(e)?e[0]??``:e)},g=(e,t,r)=>m(e,t).fold(()=>h(e,t,r),e=>n(e)),_=e=>n(e).flatMap(e=>n(e.headers)),v=(e,t)=>{let n=t.toLowerCase();return _(e).flatMap(e=>g(e,n,t)).orUndefined()},y=e=>typeof e==`string`?e:e===void 0?{content:[]}:`content`in e?e:{content:[e]},b=e=>e.content.map(e=>e.type===`text`?e.text:`[${e.type}]`).join(` `).trim()||`Tool execution failed`,x=e=>({client:{version:void 0},log:{debug:(t,n)=>e.forEach(e=>e.debug(t,n)),error:(t,n)=>e.forEach(e=>e.error(t,n)),info:(t,n)=>e.forEach(e=>e.info(t,n)),warn:(t,n)=>e.forEach(e=>e.warn(t,n))},reportProgress:async()=>{},session:void 0,streamContent:async()=>{}}),S=(t,r)=>{let{mcpPath:i}=r??{},a=i??`/mcp`,o=n(t.logger),c=new e({logger:o.orUndefined(),mcpPath:a,name:t.name,version:t.version}),l=new s;n(t.authenticate).forEach(e=>{l.use(a,p({authenticate:e}))}),l.notFound(e=>c.fetch(e.req.raw));let u=e=>o.forEach(t=>t.warn(`[somamcp/edge] ${e}`)),d=e=>u(`${e} is not supported by the edge backend; ignoring.`);return{get serverState(){return`running`},get sessions(){return[]},addPrompt:e=>{c.addPrompt({arguments:e.arguments?.map(e=>({description:e.description,name:e.name,required:e.required})),description:e.description,load:async t=>await e.load(t),name:e.name})},addResource:e=>{c.addResource({description:e.description,load:async()=>{let t=await e.load();return Array.isArray(t)?(t.length>1&&u(`resource "${e.name}" returned ${t.length} results; only the first is served.`),t[0]??{text:``}):t},mimeType:e.mimeType,name:e.name,uri:e.uri})},addResourceTemplate:()=>d(`addResourceTemplate`),addTool:e=>{let t=x(o);c.addTool({description:e.description??``,execute:async n=>{let r=y(await e.execute(n,t));if(typeof r!=`string`&&r.isError===!0)throw Error(b(r));return r},name:e.name,parameters:e.parameters})},fetch:async e=>l.fetch(e),getApp:()=>l,on:()=>{},removePrompt:()=>d(`removePrompt`),removeResource:()=>d(`removeResource`),removeTool:()=>d(`removeTool`),start:async()=>{},stop:async()=>{}}},
|
|
1
|
+
import{EdgeFastMCP as e}from"fastmcp/edge";import{Map as t,Option as n,Ref as r,Set as i,Try as a,Tuple as o}from"functype";import{Hono as s}from"hono";import{Client as c}from"@modelcontextprotocol/sdk/client/index.js";import{StreamableHTTPClientTransport as l}from"@modelcontextprotocol/sdk/client/streamableHttp.js";import{createDirectConsoleLogger as u}from"functype-log";import{z as d}from"zod";const f=e=>e.json({error:`Unauthorized`},401),p=({authenticate:e,onUnauthorized:t})=>{let r=t??f;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()}},m=(e,t)=>typeof e.get==`function`?n(e.get(t)):n.none(),h=(e,t,r)=>{let i=e;return n(i[t]??i[r]).map(e=>Array.isArray(e)?e[0]??``:e)},g=(e,t,r)=>m(e,t).fold(()=>h(e,t,r),e=>n(e)),_=e=>n(e).flatMap(e=>n(e.headers)),v=(e,t)=>{let n=t.toLowerCase();return _(e).flatMap(e=>g(e,n,t)).orUndefined()},y=e=>typeof e==`string`?e:e===void 0?{content:[]}:`content`in e?e:{content:[e]},b=e=>e.content.map(e=>e.type===`text`?e.text:`[${e.type}]`).join(` `).trim()||`Tool execution failed`,x=e=>({client:{version:void 0},log:{debug:(t,n)=>e.forEach(e=>e.debug(t,n)),error:(t,n)=>e.forEach(e=>e.error(t,n)),info:(t,n)=>e.forEach(e=>e.info(t,n)),warn:(t,n)=>e.forEach(e=>e.warn(t,n))},reportProgress:async()=>{},session:void 0,streamContent:async()=>{}}),S=(t,r)=>{let{mcpPath:i}=r??{},a=i??`/mcp`,o=n(t.logger),c=new e({logger:o.orUndefined(),mcpPath:a,name:t.name,version:t.version}),l=new s;n(t.authenticate).forEach(e=>{l.use(a,p({authenticate:e}))}),l.notFound(e=>c.fetch(e.req.raw));let u=e=>o.forEach(t=>t.warn(`[somamcp/edge] ${e}`)),d=e=>u(`${e} is not supported by the edge backend; ignoring.`);return{get serverState(){return`running`},get sessions(){return[]},addPrompt:e=>{c.addPrompt({arguments:e.arguments?.map(e=>({description:e.description,name:e.name,required:e.required})),description:e.description,load:async t=>await e.load(t),name:e.name})},addResource:e=>{c.addResource({description:e.description,load:async()=>{let t=await e.load();return Array.isArray(t)?(t.length>1&&u(`resource "${e.name}" returned ${t.length} results; only the first is served.`),t[0]??{text:``}):t},mimeType:e.mimeType,name:e.name,uri:e.uri})},addResourceTemplate:()=>d(`addResourceTemplate`),addTool:e=>{let t=x(o);c.addTool({description:e.description??``,execute:async n=>{let r=y(await e.execute(n,t));if(typeof r!=`string`&&r.isError===!0)throw Error(b(r));return r},name:e.name,parameters:e.parameters})},fetch:async e=>l.fetch(e),getApp:()=>l,on:()=>{},removePrompt:()=>d(`removePrompt`),removeResource:()=>d(`removeResource`),removeTool:()=>d(`removeTool`),supportsRemoval:!1,start:async()=>{},stop:async()=>{}}},ee=(e,t,n)=>{t.forEach(t=>{switch(t.protected&&e.use(t.path,p({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))}})},te=(e,t,n)=>({handler:()=>{let r=e(),i=t(),a=n(),o=ne(r,i,a);return new Response(o,{headers:{"Content-Type":`text/html; charset=utf-8`}})},path:`/dashboard`,protected:!0,type:`dynamic`}),ne=(e,t,n)=>`<!DOCTYPE html>
|
|
2
2
|
<html lang="en">
|
|
3
3
|
<head>
|
|
4
4
|
<meta charset="UTF-8">
|
|
@@ -32,7 +32,7 @@ import{EdgeFastMCP as e}from"fastmcp/edge";import{Map as t,Option as n,Ref as r,
|
|
|
32
32
|
<div class="card">
|
|
33
33
|
<h2>Health</h2>
|
|
34
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">${
|
|
35
|
+
<div class="metric"><span class="metric-label">Uptime:</span> <span class="metric-value">${C(e.uptime)}</span></div>
|
|
36
36
|
<div class="metric"><span class="metric-label">Sessions:</span> <span class="metric-value">${e.activeSessions}</span></div>
|
|
37
37
|
<div class="metric"><span class="metric-label">Gateways:</span> <span class="metric-value">${e.gateways.connected}/${e.gateways.total}</span></div>
|
|
38
38
|
</div>
|
|
@@ -54,8 +54,8 @@ import{EdgeFastMCP as e}from"fastmcp/edge";import{Map as t,Option as n,Ref as r,
|
|
|
54
54
|
</div>`:``}
|
|
55
55
|
</div>
|
|
56
56
|
</body>
|
|
57
|
-
</html>`,T=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`},E=`/health`,D=`/health/detail`,te=(e,t=E)=>({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`}),O=(e,t=D)=>({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=()=>j().flatMap(e=>n(e.versions?.node)).isEmpty===!1,N=e=>j().flatMap(t=>n(t.env?.[e])).filter(e=>e.length>0),P=()=>({branch:N(`SOMAMCP_BUILD_BRANCH`).orUndefined(),commit:N(`SOMAMCP_BUILD_COMMIT`).orUndefined(),date:N(`SOMAMCP_BUILD_DATE`).orUndefined(),environment:N(`SOMAMCP_ENVIRONMENT`).orUndefined()}),ne=e=>{let t=P();return{branch:e?.branch??t.branch,commit:e?.commit??t.commit,date:e?.date??t.date,environment:e?.environment??t.environment}},F=`unknown`,I=()=>M()?{arch:j().flatMap(e=>n(e.arch)).orElse(F),nodeVersion:j().flatMap(e=>n(e.version)).orElse(F),platform:j().flatMap(e=>n(e.platform)).orElse(F),runtime:`node`}:{arch:F,nodeVersion:F,platform:n(globalThis.navigator?.userAgent).orElse(F),runtime:`edge`},L=(e,t)=>{let i=r(n.none()),o=r(n.none()),s=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:s.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 s.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(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 l(new URL(e.url));o.set(n(t));let r=new c({name:`soma-gateway-${e.id}`,version:`1.0.0`});i.set(n(r)),await r.connect(t);let a=await r.listTools();s.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:s.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()),s.set([]),u.set(`disconnected`),t.recordEvent({data:{id:e.id},name:f,timestamp:Date.now(),type:`gateway.disconnect`})}};return m},R=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=L(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)}}},z=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`},B=(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`]}},V=(e,t)=>{let n=z(e),r=e instanceof Error?e.message:String(e),i=B(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}},H=e=>a.fromPromise(e),U=(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 H(e.callTool(r.name,i))).fold(n=>{let s=V(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}`}))},W=(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}),G=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)}}),K={flush:()=>Promise.resolve(),recordEvent:()=>{}},q=(e,t)=>!t||t.length===0?e:Object.fromEntries(Object.entries(e).map(([e,n])=>[e,t.includes(e)?`[REDACTED]`:n])),re=(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},J=e=>a.fromPromise(e),Y=(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`?q(r,n?.redactInputFields):void 0;return(await J(e.execute(r,i))).fold(n=>{let r=V(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`?re(r,n?.maxOutputSize):void 0,requestId:s,sessionId:c,timestamp:o,type:`tool.execute`}),r))}}),X=(e,t)=>({...e,load:async n=>{let r=Date.now();return(await J(e.load(n))).fold(n=>{let i=V(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))}}),Z=(e,t)=>({...e,load:async(n,r)=>{let i=Date.now();return(await J(e.load(n,r))).fold(n=>{let r=V(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))}}),ie=e=>{let{artifacts:t,backend:i,backendOptions:a,build:o,enableDashboard:s,enableHealthEndpoint:c,enableInfoEndpoint:l,enableIntrospection:u,gateways:d,healthDetailPath:f,healthPath:m,infoPath:h,introspectionPrefix:g,logLayer:_,telemetry:v,...y}=e,b=y.name,x=y.version,S=ne(o),ee=I(),T=g??``,E=v??(_?G(_):K),D=i(y,a),k=R(E),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:D.sessions.length,gateways:{connected:k.connectedCount,total:k.totalCount},name:b,startedAt:e,status:D.serverState,uptime:t}},L=()=>({prompts:N,resources:M,tools:j}),z=()=>({build:S,capabilities:{prompts:N.length,resources:M.length,tools:j.length},name:b,runtime:ee,version:x});u!==!1&&D.addTool(W(()=>z(),`${T}info`));let B=[...t??[]];c!==!1&&(B.push(te(()=>F(),m)),B.push(O(()=>F(),f))),l!==!1&&B.push(A(()=>z(),h)),s!==!1&&B.push(w(()=>F(),()=>L(),()=>k.getInfoAll())),B.length>0&&C(D.getApp(),B,n(y.authenticate).orUndefined()),D.on(`connect`,({session:e})=>{E.recordEvent({data:{sessionId:e.sessionId},name:b,timestamp:Date.now(),type:`session.connect`})}),D.on(`disconnect`,({session:e})=>{E.recordEvent({data:{sessionId:e.sessionId},name:b,timestamp:Date.now(),type:`session.disconnect`})});let V=e=>{let t=`captureConfig`in e?e.captureConfig:void 0,n=Y(e,E,t);D.addTool(n),j.push({description:e.description,name:e.name})},H=e=>{let t=X(e,E);D.addResource(t),M.push({description:e.description,name:e.name,uri:e.uri})},q=e=>{let t=Z(e,E);D.addPrompt(t),N.push({description:e.description,name:e.name})};return{get name(){return b},get serverState(){return D.serverState},get sessions(){return D.sessions},addPrompt:q,addPrompts:e=>{e.forEach(q)},addResource:H,addResources:e=>{e.forEach(H)},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(y.authenticate).orUndefined();t.use(e.path,p({authenticate:r,onUnauthorized:e.onUnauthorized}))}t.on(r,e.path,t=>e.handler(t))},addTool:V,addTools:e=>{e.forEach(e=>V(e))},fetch:async e=>D.fetch?D.fetch(e):D.getApp().fetch(e),getApp:()=>D.getApp(),getCapabilities:L,getGatewayManager:()=>k,getHealth:F,getInfo:z,removePrompt:e=>{D.removePrompt(e)},removeResource:e=>{D.removeResource(e)},removeTool:e=>{D.removeTool(e)},async start(e){P.set(Date.now()),E.recordEvent({name:b,timestamp:P.get(),type:`server.start`}),await D.start(e),await k.connectAll(),k.getAll().filter(e=>e.status===`connected`&&e.config.proxyTools!==!1).forEach(e=>{U(e,E).forEach(e=>D.addTool(e))})},async stop(){await k.disconnectAll(),await D.stop(),E.recordEvent({name:b,timestamp:Date.now(),type:`server.stop`}),E.flush&&await E.flush()}}};var ae=class extends Error{constructor(e){super(e),this.name=`UserError`}};const oe=e=>({flush:async()=>{await Promise.all(e.map(e=>e.flush?e.flush():Promise.resolve()))},recordEvent:t=>{e.forEach(e=>e.recordEvent(t))}}),se=(e=`[soma]`)=>{let t=u({prefix:e,stream:`stderr`});return G(t)},ce=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`}),le=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`}),ue=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}},de=[`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(`
|
|
58
|
-
`),
|
|
59
|
-
`))},
|
|
60
|
-
`)),
|
|
61
|
-
`),
|
|
57
|
+
</html>`,C=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`},w=`/health`,T=`/health/detail`,E=(e,t=w)=>({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`}),D=(e,t=T)=>({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`}),O=`/info`,k=(e,t=O)=>({handler:()=>new Response(JSON.stringify(e()),{headers:{"Content-Type":`application/json`},status:200}),path:t,protected:!0,type:`dynamic`}),A=()=>n(globalThis.process),j=e=>n(globalThis[e]),M=()=>j(`navigator`).flatMap(e=>n(e.userAgent)),N=()=>M().contains(`Cloudflare-Workers`)||j(`Deno`).isEmpty===!1||j(`EdgeRuntime`).isEmpty===!1,P=()=>!N()&&A().flatMap(e=>n(e.versions?.node)).isEmpty===!1,F=e=>A().flatMap(t=>n(t.env?.[e])).filter(e=>e.length>0),I=()=>({branch:F(`SOMAMCP_BUILD_BRANCH`).orUndefined(),commit:F(`SOMAMCP_BUILD_COMMIT`).orUndefined(),date:F(`SOMAMCP_BUILD_DATE`).orUndefined(),environment:F(`SOMAMCP_ENVIRONMENT`).orUndefined()}),L=e=>{let t=I();return{branch:e?.branch??t.branch,commit:e?.commit??t.commit,date:e?.date??t.date,environment:e?.environment??t.environment}},R=`unknown`,z=()=>P()?{arch:A().flatMap(e=>n(e.arch)).orElse(R),nodeVersion:A().flatMap(e=>n(e.version)).orElse(R),platform:A().flatMap(e=>n(e.platform)).orElse(R),runtime:`node`}:{arch:R,nodeVersion:R,platform:M().orElse(R),runtime:`edge`},B=(e,t)=>{let i=r(n.none()),o=r(n.none()),s=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:s.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 s.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(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 l(new URL(e.url));o.set(n(t));let r=new c({name:`soma-gateway-${e.id}`,version:`1.0.0`});i.set(n(r)),await r.connect(t);let a=await r.listTools();s.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:s.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()),s.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:m,infoPath:h,introspectionPrefix:g,logLayer:_,telemetry:v,...y}=e,b=y.name,x=y.version,S=L(o),ne=z(),C=g??``,w=v??(_?J(_):Y),T=i(y,a),O=V(w),A=[],j=[],M=[],N=r(0);d?.forEach(e=>O.add(e));let P=()=>{let e=N.get(),t=e>0?Date.now()-e:0;return{activeSessions:T.sessions.length,gateways:{connected:O.connectedCount,total:O.totalCount},name:b,startedAt:e,status:T.serverState,uptime:t}},F=()=>({prompts:M,resources:j,tools:A}),I=()=>({build:S,capabilities:{prompts:M.length,resources:j.length,tools:A.length},name:b,runtime:ne,version:x}),R=e=>{A.some(t=>t.name===e.name)||A.push({description:e.description,name:e.name})},B=(e,t)=>{if(T.supportsRemoval===!1)return;let n=e.findIndex(e=>e.name===t);n>=0&&e.splice(n,1)};if(u!==!1){let e=q(()=>I(),`${C}info`);T.addTool(e),R(e)}let H=[...t??[]];c!==!1&&(H.push(E(()=>P(),m)),H.push(D(()=>P(),f))),l!==!1&&H.push(k(()=>I(),h)),s!==!1&&H.push(te(()=>P(),()=>F(),()=>O.getInfoAll())),H.length>0&&ee(T.getApp(),H,n(y.authenticate).orUndefined()),T.on(`connect`,({session:e})=>{w.recordEvent({data:{sessionId:e.sessionId},name:b,timestamp:Date.now(),type:`session.connect`})}),T.on(`disconnect`,({session:e})=>{w.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,w,t);T.addTool(n),R(e)},W=e=>{let t=ae(e,w);T.addResource(t),j.push({description:e.description,name:e.name,uri:e.uri})},G=e=>{let t=oe(e,w);T.addPrompt(t),M.push({description:e.description,name:e.name})};return{get name(){return b},get serverState(){return T.serverState},get sessions(){return T.sessions},addPrompt:G,addPrompts:e=>{e.forEach(G)},addResource:W,addResources:e=>{e.forEach(W)},addResourceTemplate:(...e)=>{T.addResourceTemplate(...e)},addRoute:e=>{let t=T.getApp(),r=Array.isArray(e.method)?[...e.method]:[e.method];if(e.protected){let r=n(y.authenticate).orUndefined();t.use(e.path,p({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=>T.fetch?T.fetch(e):T.getApp().fetch(e),getApp:()=>T.getApp(),getCapabilities:F,getGatewayManager:()=>O,getHealth:P,getInfo:I,removePrompt:e=>{T.removePrompt(e),B(M,e)},removeResource:e=>{T.removeResource(e),B(j,e)},removeTool:e=>{T.removeTool(e),B(A,e)},async start(e){N.set(Date.now()),w.recordEvent({name:b,timestamp:N.get(),type:`server.start`}),await T.start(e),await O.connectAll(),O.getAll().filter(e=>e.status===`connected`&&e.config.proxyTools!==!1).forEach(e=>{K(e,w).forEach(e=>{T.addTool(e),R(e)})})},async stop(){await O.disconnectAll(),await T.stop(),w.recordEvent({name:b,timestamp:Date.now(),type:`server.stop`}),w.flush&&await w.flush()}}};var ce=class extends Error{constructor(e){super(e),this.name=`UserError`}};const le=e=>({flush:async()=>{await Promise.all(e.map(e=>e.flush?e.flush():Promise.resolve()))},recordEvent:t=>{e.forEach(e=>e.recordEvent(t))}}),ue=(e=`[soma]`)=>{let t=u({prefix:e,stream:`stderr`});return J(t)},de=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`}),fe=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`}),pe=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}},me=[`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(`
|
|
58
|
+
`),he=d.object({description:d.string().min(10,`Provide enough context to reproduce (>=10 chars)`).max(8e3).describe(`Detailed description. NO secrets, customer data, or internal URLs.`),severity:d.enum([`low`,`medium`,`high`]).optional().describe(`Severity hint (optional)`),title:d.string().min(5,`Title must be at least 5 characters`).max(200).describe(`Brief, public-safe summary`),type:d.enum([`bug`,`feature`,`feedback`,`question`]).default(`feedback`).describe(`What kind of report this is`)}),ge=(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(`
|
|
59
|
+
`))},_e=e=>e.filter(e=>Object.keys(e).length>0).map(e=>[`---`,`**Context (auto-collected):**`,"```json",JSON.stringify(e,null,2),"```"].join(`
|
|
60
|
+
`)),ve=(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(`
|
|
61
|
+
`),ye=e=>{let t=e.redactionPatterns??Q;return{annotations:{destructiveHint:!1,readOnlyHint:!1},description:e.description??me,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=ge(i,a),c=_e(o),l=ve(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:he}},be=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(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}})}}},xe=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}})}},Se=e=>se({...e,backend:e.backend??S});export{T as DEFAULT_HEALTH_DETAIL_PATH,w as DEFAULT_HEALTH_PATH,O as DEFAULT_INFO_PATH,Q as DEFAULT_REDACTION_PATTERNS,Y as NoopTelemetry,ce as UserError,H as classifyError,p as createAuthMiddleware,de as createCapabilitiesTool,le as createCompositeTelemetry,fe as createConnectionsTool,ue as createConsoleTelemetry,te as createDashboardArtifact,S as createEdgeBackend,W as createEnrichedError,ye as createFeedbackTool,B as createGateway,V as createGatewayManager,be as createGithubFeedback,E as createHealthArtifact,D as createHealthDetailArtifact,pe as createHealthTool,k as createInfoArtifact,q as createInfoTool,K as createProxiedTools,Se as createServer,se as createServerCore,xe as createWebhookFeedback,v as getRequestHeader,z as getRuntimeInfo,I as readBuildInfoFromEnv,$ as redact,ee as registerArtifacts,L as resolveBuildInfo,oe as wrapPrompt,ae as wrapResource,Z as wrapTool};
|
|
@@ -226,6 +226,15 @@ type BackendAdapter<T extends SessionAuth = SessionAuth> = {
|
|
|
226
226
|
fetch?: (request: Request) => Promise<Response>;
|
|
227
227
|
getApp: () => Hono;
|
|
228
228
|
on: <E extends keyof BackendEvents<T>>(event: E, handler: BackendEvents<T>[E]) => void;
|
|
229
|
+
/**
|
|
230
|
+
* Whether `removeTool` / `removeResource` / `removePrompt` actually take
|
|
231
|
+
* effect. Defaults to true when absent.
|
|
232
|
+
*
|
|
233
|
+
* The edge backend sets this false: EdgeFastMCP exposes no removal API, so its
|
|
234
|
+
* `remove*` calls only warn. Capability counts must not shrink for a tool that
|
|
235
|
+
* is still being served.
|
|
236
|
+
*/
|
|
237
|
+
readonly supportsRemoval?: boolean;
|
|
229
238
|
removePrompt: (name: string) => void;
|
|
230
239
|
removeResource: (name: string) => void;
|
|
231
240
|
removeTool: (name: string) => void;
|
package/dist/index.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { A as ToolAnnotations, C as ResourceLink, D as SessionAuth, E as ServerStatus, O as TextContent, S as ResourceContent, T as SchemaParams, _ as Progress, a as BackendSession, b as PromptResult, c as ServerConfig, d as Completion, f as Content, g as InferSchemaOutput, h as ImageContent, i as BackendFactory, j as UserError, k as Tool, l as TransportConfig, m as Context, n as BackendAdapter, o as HttpStreamConfig, p as ContentResult, s as Logger, t as createFastMCPBackend, u as AudioContent, v as Prompt, w as ResourceResult, x as Resource, y as PromptArgument } from "./index-
|
|
1
|
+
import { A as ToolAnnotations, C as ResourceLink, D as SessionAuth, E as ServerStatus, O as TextContent, S as ResourceContent, T as SchemaParams, _ as Progress, a as BackendSession, b as PromptResult, c as ServerConfig, d as Completion, f as Content, g as InferSchemaOutput, h as ImageContent, i as BackendFactory, j as UserError, k as Tool, l as TransportConfig, m as Context, n as BackendAdapter, o as HttpStreamConfig, p as ContentResult, s as Logger, t as createFastMCPBackend, u as AudioContent, v as Prompt, w as ResourceResult, x as Resource, y as PromptArgument } from "./index-pl2m5zH7.js";
|
|
2
2
|
import { Option } from "functype";
|
|
3
3
|
import { DirectLogger, DirectLogger as DirectLogger$1 } from "functype-log";
|
|
4
4
|
import { z } from "zod";
|
package/dist/index.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import{t as e}from"./backend-CA7j4YCF.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()},
|
|
1
|
+
import{t as e}from"./backend-CA7j4YCF.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
2
|
<html lang="en">
|
|
3
3
|
<head>
|
|
4
4
|
<meta charset="UTF-8">
|
|
@@ -32,7 +32,7 @@ import{t as e}from"./backend-CA7j4YCF.js";import{Map as t,Option as n,Ref as r,S
|
|
|
32
32
|
<div class="card">
|
|
33
33
|
<h2>Health</h2>
|
|
34
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">${
|
|
35
|
+
<div class="metric"><span class="metric-label">Uptime:</span> <span class="metric-value">${E(e.uptime)}</span></div>
|
|
36
36
|
<div class="metric"><span class="metric-label">Sessions:</span> <span class="metric-value">${e.activeSessions}</span></div>
|
|
37
37
|
<div class="metric"><span class="metric-label">Gateways:</span> <span class="metric-value">${e.gateways.connected}/${e.gateways.total}</span></div>
|
|
38
38
|
</div>
|
|
@@ -54,9 +54,9 @@ import{t as e}from"./backend-CA7j4YCF.js";import{Map as t,Option as n,Ref as r,S
|
|
|
54
54
|
</div>`:``}
|
|
55
55
|
</div>
|
|
56
56
|
</body>
|
|
57
|
-
</html>`,T=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`},E=`/health`,D=`/health/detail`,te=(e,t=E)=>({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`}),O=(e,t=D)=>({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=()=>j().flatMap(e=>n(e.versions?.node)).isEmpty===!1,N=e=>j().flatMap(t=>n(t.env?.[e])).filter(e=>e.length>0),P=()=>({branch:N(`SOMAMCP_BUILD_BRANCH`).orUndefined(),commit:N(`SOMAMCP_BUILD_COMMIT`).orUndefined(),date:N(`SOMAMCP_BUILD_DATE`).orUndefined(),environment:N(`SOMAMCP_ENVIRONMENT`).orUndefined()}),F=e=>{let t=P();return{branch:e?.branch??t.branch,commit:e?.commit??t.commit,date:e?.date??t.date,environment:e?.environment??t.environment}},I=`unknown`,ne=()=>M()?{arch:j().flatMap(e=>n(e.arch)).orElse(I),nodeVersion:j().flatMap(e=>n(e.version)).orElse(I),platform:j().flatMap(e=>n(e.platform)).orElse(I),runtime:`node`}:{arch:I,nodeVersion:I,platform:n(globalThis.navigator?.userAgent).orElse(I),runtime:`edge`},L=(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(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},R=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=L(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)}}},z=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`},B=(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`]}},V=(e,t)=>{let n=z(e),r=e instanceof Error?e.message:String(e),i=B(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}},H=e=>a.fromPromise(e),U=(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 H(e.callTool(r.name,i))).fold(n=>{let s=V(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}`}))},W=(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}),G=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)}}),K={flush:()=>Promise.resolve(),recordEvent:()=>{}},q=(e,t)=>!t||t.length===0?e:Object.fromEntries(Object.entries(e).map(([e,n])=>[e,t.includes(e)?`[REDACTED]`:n])),re=(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},J=e=>a.fromPromise(e),Y=(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`?q(r,n?.redactInputFields):void 0;return(await J(e.execute(r,i))).fold(n=>{let r=V(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`?re(r,n?.maxOutputSize):void 0,requestId:s,sessionId:c,timestamp:o,type:`tool.execute`}),r))}}),X=(e,t)=>({...e,load:async n=>{let r=Date.now();return(await J(e.load(n))).fold(n=>{let i=V(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))}}),Z=(e,t)=>({...e,load:async(n,r)=>{let i=Date.now();return(await J(e.load(n,r))).fold(n=>{let r=V(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))}}),ie=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=F(o),w=ne(),T=h??``,E=v??(g?G(g):K),D=i(y,a),k=R(E),j=[],M=[],N=[],P=r(0);d?.forEach(e=>k.add(e));let I=()=>{let e=P.get(),t=e>0?Date.now()-e:0;return{activeSessions:D.sessions.length,gateways:{connected:k.connectedCount,total:k.totalCount},name:b,startedAt:e,status:D.serverState,uptime:t}},L=()=>({prompts:N,resources:M,tools:j}),z=()=>({build:S,capabilities:{prompts:N.length,resources:M.length,tools:j.length},name:b,runtime:w,version:x});u!==!1&&D.addTool(W(()=>z(),`${T}info`));let B=[...t??[]];c!==!1&&(B.push(te(()=>I(),p)),B.push(O(()=>I(),f))),l!==!1&&B.push(A(()=>z(),m)),s!==!1&&B.push(C(()=>I(),()=>L(),()=>k.getInfoAll())),B.length>0&&ee(D.getApp(),B,n(y.authenticate).orUndefined()),D.on(`connect`,({session:e})=>{E.recordEvent({data:{sessionId:e.sessionId},name:b,timestamp:Date.now(),type:`session.connect`})}),D.on(`disconnect`,({session:e})=>{E.recordEvent({data:{sessionId:e.sessionId},name:b,timestamp:Date.now(),type:`session.disconnect`})});let V=e=>{let t=`captureConfig`in e?e.captureConfig:void 0,n=Y(e,E,t);D.addTool(n),j.push({description:e.description,name:e.name})},H=e=>{let t=X(e,E);D.addResource(t),M.push({description:e.description,name:e.name,uri:e.uri})},q=e=>{let t=Z(e,E);D.addPrompt(t),N.push({description:e.description,name:e.name})};return{get name(){return b},get serverState(){return D.serverState},get sessions(){return D.sessions},addPrompt:q,addPrompts:e=>{e.forEach(q)},addResource:H,addResources:e=>{e.forEach(H)},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(y.authenticate).orUndefined();t.use(e.path,_({authenticate:r,onUnauthorized:e.onUnauthorized}))}t.on(r,e.path,t=>e.handler(t))},addTool:V,addTools:e=>{e.forEach(e=>V(e))},fetch:async e=>D.fetch?D.fetch(e):D.getApp().fetch(e),getApp:()=>D.getApp(),getCapabilities:L,getGatewayManager:()=>k,getHealth:I,getInfo:z,removePrompt:e=>{D.removePrompt(e)},removeResource:e=>{D.removeResource(e)},removeTool:e=>{D.removeTool(e)},async start(e){P.set(Date.now()),E.recordEvent({name:b,timestamp:P.get(),type:`server.start`}),await D.start(e),await k.connectAll(),k.getAll().filter(e=>e.status===`connected`&&e.config.proxyTools!==!1).forEach(e=>{U(e,E).forEach(e=>D.addTool(e))})},async stop(){await k.disconnectAll(),await D.stop(),E.recordEvent({name:b,timestamp:Date.now(),type:`server.stop`}),E.flush&&await E.flush()}}};var ae=class extends Error{constructor(e){super(e),this.name=`UserError`}};const oe={".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`},se={".aac":`audio/aac`,".flac":`audio/flac`,".m4a":`audio/mp4`,".mp3":`audio/mpeg`,".ogg":`audio/ogg`,".opus":`audio/opus`,".wav":`audio/wav`,".weba":`audio/webm`},ce=(e,t,n)=>t[p(new URL(e,`file:///`).pathname).toLowerCase()]??n,le=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}},ue=async e=>{let{data:t,source:n}=await le(e,`image`),r=n?ce(n,oe,`image/png`):`image/png`;return{data:t.toString(`base64`),mimeType:r,type:`image`}},de=async e=>{let{data:t,source:n}=await le(e,`audio`),r=n?ce(n,se,`audio/mpeg`):`audio/mpeg`;return{data:t.toString(`base64`),mimeType:r,type:`audio`}},fe=e=>({flush:async()=>{await Promise.all(e.map(e=>e.flush?e.flush():Promise.resolve()))},recordEvent:t=>{e.forEach(e=>e.recordEvent(t))}}),pe=(e=`[soma]`)=>{let t=m({prefix:e,stream:`stderr`});return G(t)},me=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()}}},
|
|
59
|
-
`),
|
|
60
|
-
`))},
|
|
61
|
-
`)),
|
|
62
|
-
`),
|
|
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(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(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};
|