somamcp 1.0.0 → 1.0.1
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 +238 -0
- package/dist/index.d.ts +7 -1
- package/package.json +1 -1
package/README.md
ADDED
|
@@ -0,0 +1,238 @@
|
|
|
1
|
+
# somamcp
|
|
2
|
+
|
|
3
|
+
Framework for building **MCP (Model Context Protocol)** servers with built-in telemetry, introspection, and a backend abstraction layer.
|
|
4
|
+
|
|
5
|
+
somamcp wraps an underlying MCP framework (currently [FastMCP](https://github.com/punkpeye/fastmcp)) behind a `BackendAdapter` interface — giving you a stable API surface, automatic telemetry, gateway-based server composition, and HTTP artifacts out of the box.
|
|
6
|
+
|
|
7
|
+
## Features
|
|
8
|
+
|
|
9
|
+
- **Backend abstraction** — framework-agnostic `BackendAdapter` interface; swap the underlying MCP framework without changing consumer code
|
|
10
|
+
- **Structured telemetry** — pluggable `TelemetryCollector` with console, file (NDJSON), composite, and [functype-log](https://github.com/jordanburke/functype-log) adapters
|
|
11
|
+
- **Error classification & enrichment** — errors are auto-classified (validation / timeout / gateway / auth / not_found / internal) with actionable suggestions for LLMs
|
|
12
|
+
- **Per-tool capture config** — configure input/output capture levels, field redaction, and output size limits per tool
|
|
13
|
+
- **Gateway system** — connect to remote MCP servers and proxy their tools as local tools
|
|
14
|
+
- **Auto-introspection** — `soma_health`, `soma_capabilities`, `soma_connections` tools registered by default
|
|
15
|
+
- **HTTP artifacts** — mount static or dynamic routes on the embedded [Hono](https://hono.dev) app, including an auto-generated dashboard
|
|
16
|
+
- **Functional style** — powered by [functype](https://github.com/jordanburke/functype) (`Ref`, `Try`, `Either`)
|
|
17
|
+
|
|
18
|
+
## Installation
|
|
19
|
+
|
|
20
|
+
```bash
|
|
21
|
+
pnpm add somamcp
|
|
22
|
+
# or
|
|
23
|
+
npm install somamcp
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
## Quick Start
|
|
27
|
+
|
|
28
|
+
```typescript
|
|
29
|
+
import { createServer } from "somamcp"
|
|
30
|
+
import { z } from "zod"
|
|
31
|
+
|
|
32
|
+
const server = createServer({
|
|
33
|
+
name: "my-server",
|
|
34
|
+
version: "1.0.0",
|
|
35
|
+
})
|
|
36
|
+
|
|
37
|
+
server.addTool({
|
|
38
|
+
name: "greet",
|
|
39
|
+
description: "Greet someone by name",
|
|
40
|
+
parameters: z.object({ name: z.string() }),
|
|
41
|
+
execute: async ({ name }) => `Hello, ${name}!`,
|
|
42
|
+
})
|
|
43
|
+
|
|
44
|
+
await server.start({ transportType: "stdio" })
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
## Telemetry
|
|
48
|
+
|
|
49
|
+
Telemetry is opt-in via the `telemetry` option. Every tool/resource/prompt call is automatically wrapped with timing, error classification, and optional input/output capture.
|
|
50
|
+
|
|
51
|
+
```typescript
|
|
52
|
+
import { createServer, createCompositeTelemetry, createConsoleTelemetry, createJsonFileTelemetry } from "somamcp"
|
|
53
|
+
|
|
54
|
+
const telemetry = createCompositeTelemetry([
|
|
55
|
+
createConsoleTelemetry(),
|
|
56
|
+
createJsonFileTelemetry({ filePath: "./logs/events.ndjson" }),
|
|
57
|
+
])
|
|
58
|
+
|
|
59
|
+
const server = createServer({
|
|
60
|
+
name: "my-server",
|
|
61
|
+
version: "1.0.0",
|
|
62
|
+
telemetry,
|
|
63
|
+
})
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
### Per-tool capture configuration
|
|
67
|
+
|
|
68
|
+
```typescript
|
|
69
|
+
server.addTool({
|
|
70
|
+
name: "processPayment",
|
|
71
|
+
description: "Process a payment",
|
|
72
|
+
parameters: z.object({ amount: z.number(), cardToken: z.string() }),
|
|
73
|
+
execute: async (args) => {
|
|
74
|
+
/* ... */
|
|
75
|
+
},
|
|
76
|
+
captureConfig: {
|
|
77
|
+
captureLevel: "full",
|
|
78
|
+
redactInputFields: ["cardToken"],
|
|
79
|
+
maxOutputSize: 2000,
|
|
80
|
+
},
|
|
81
|
+
})
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
Capture levels:
|
|
85
|
+
|
|
86
|
+
- `"full"` — input + output + metadata (default)
|
|
87
|
+
- `"metadata"` — timing + name + ids only
|
|
88
|
+
- `"none"` — no telemetry
|
|
89
|
+
|
|
90
|
+
### Error enrichment
|
|
91
|
+
|
|
92
|
+
Errors thrown from tools are automatically classified and returned as structured `ContentResult` responses with `isError: true`, including suggestions to help calling LLMs self-correct.
|
|
93
|
+
|
|
94
|
+
```typescript
|
|
95
|
+
// Thrown: new Error("Request timed out after 30s")
|
|
96
|
+
// Returned to LLM:
|
|
97
|
+
{
|
|
98
|
+
isError: true,
|
|
99
|
+
content: [{
|
|
100
|
+
type: "text",
|
|
101
|
+
text: JSON.stringify({
|
|
102
|
+
errorCategory: "timeout",
|
|
103
|
+
message: "Request timed out after 30s",
|
|
104
|
+
suggestions: [
|
|
105
|
+
"The operation took too long. Try reducing the scope of the request.",
|
|
106
|
+
"Check if the upstream service is responsive.",
|
|
107
|
+
],
|
|
108
|
+
}),
|
|
109
|
+
}],
|
|
110
|
+
}
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
## Gateways
|
|
114
|
+
|
|
115
|
+
Connect to remote MCP servers and proxy their tools as local tools.
|
|
116
|
+
|
|
117
|
+
```typescript
|
|
118
|
+
const server = createServer({
|
|
119
|
+
name: "gateway-server",
|
|
120
|
+
version: "1.0.0",
|
|
121
|
+
gateways: [
|
|
122
|
+
{
|
|
123
|
+
id: "upstream",
|
|
124
|
+
url: "https://remote-mcp.example.com",
|
|
125
|
+
proxyTools: true, // register remote tools as local (prefixed: "upstream_toolname")
|
|
126
|
+
reconnect: true,
|
|
127
|
+
},
|
|
128
|
+
],
|
|
129
|
+
})
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
## Artifacts & Dashboard
|
|
133
|
+
|
|
134
|
+
Mount HTTP routes on the embedded Hono app. A health dashboard is auto-mounted at `/dashboard` unless disabled.
|
|
135
|
+
|
|
136
|
+
```typescript
|
|
137
|
+
const server = createServer({
|
|
138
|
+
name: "my-server",
|
|
139
|
+
version: "1.0.0",
|
|
140
|
+
enableDashboard: true, // default
|
|
141
|
+
artifacts: [
|
|
142
|
+
{
|
|
143
|
+
type: "dynamic",
|
|
144
|
+
path: "/status",
|
|
145
|
+
handler: (c) => c.json({ ok: true }),
|
|
146
|
+
},
|
|
147
|
+
],
|
|
148
|
+
})
|
|
149
|
+
|
|
150
|
+
await server.start({
|
|
151
|
+
transportType: "httpStream",
|
|
152
|
+
httpStream: { port: 8080 },
|
|
153
|
+
})
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
## Introspection
|
|
157
|
+
|
|
158
|
+
Three tools are auto-registered (disable with `enableIntrospection: false`):
|
|
159
|
+
|
|
160
|
+
- **`soma_health`** — server status, uptime, active sessions, gateway count
|
|
161
|
+
- **`soma_capabilities`** — registered tools, resources, prompts
|
|
162
|
+
- **`soma_connections`** — gateway connection info
|
|
163
|
+
|
|
164
|
+
## Backend Abstraction
|
|
165
|
+
|
|
166
|
+
The underlying MCP framework is accessed exclusively through the `BackendAdapter` interface. To use a non-FastMCP backend, implement `BackendFactory`:
|
|
167
|
+
|
|
168
|
+
```typescript
|
|
169
|
+
import { createFastMCPBackend } from "somamcp/backend"
|
|
170
|
+
import type { BackendFactory } from "somamcp/backend"
|
|
171
|
+
|
|
172
|
+
const myBackend: BackendFactory = (config, backendOptions) => {
|
|
173
|
+
// ...
|
|
174
|
+
}
|
|
175
|
+
```
|
|
176
|
+
|
|
177
|
+
Framework-specific options (e.g. FastMCP's OAuth, ping, health endpoints) flow through `backendOptions`:
|
|
178
|
+
|
|
179
|
+
```typescript
|
|
180
|
+
createServer({
|
|
181
|
+
name: "my-server",
|
|
182
|
+
version: "1.0.0",
|
|
183
|
+
backendOptions: {
|
|
184
|
+
ping: { enabled: true, intervalMs: 30000 },
|
|
185
|
+
// ...any FastMCP ServerOptions fields
|
|
186
|
+
},
|
|
187
|
+
})
|
|
188
|
+
```
|
|
189
|
+
|
|
190
|
+
## API Surface
|
|
191
|
+
|
|
192
|
+
**Core**
|
|
193
|
+
|
|
194
|
+
- `createServer(options)` → `SomaServerInstance`
|
|
195
|
+
- Types: `SomaServerOptions`, `SomaServerInstance`, `ServerHealth`, `ServerCapabilities`, `ToolOptions`
|
|
196
|
+
|
|
197
|
+
**Primitives** (somamcp-owned, no FastMCP leakage)
|
|
198
|
+
|
|
199
|
+
- `Tool`, `Resource`, `Prompt`, `Context`, `Content`, `ContentResult`, `SessionAuth`, `UserError`
|
|
200
|
+
|
|
201
|
+
**Telemetry**
|
|
202
|
+
|
|
203
|
+
- `createConsoleTelemetry`, `createJsonFileTelemetry`, `createCompositeTelemetry`, `createLogLayerTelemetry`, `NoopTelemetry`
|
|
204
|
+
- `classifyError`, `createEnrichedError`
|
|
205
|
+
- Types: `TelemetryCollector`, `TelemetryEvent`, `CaptureLevel`, `ToolCaptureConfig`, `ErrorCategory`
|
|
206
|
+
|
|
207
|
+
**Gateway**
|
|
208
|
+
|
|
209
|
+
- `createGateway`, `createGatewayManager`, `createProxiedTools`
|
|
210
|
+
- Types: `GatewayConfig`, `GatewayInstance`, `GatewayManagerInstance`, `GatewayStatus`
|
|
211
|
+
|
|
212
|
+
**Artifacts**
|
|
213
|
+
|
|
214
|
+
- `registerArtifacts`, `createDashboardArtifact`
|
|
215
|
+
- Types: `StaticArtifact`, `DynamicArtifact`, `DirectoryArtifact`, `ArtifactConfig`
|
|
216
|
+
|
|
217
|
+
**Content helpers**
|
|
218
|
+
|
|
219
|
+
- `imageContent`, `audioContent`
|
|
220
|
+
|
|
221
|
+
**Backend** (via `somamcp/backend`)
|
|
222
|
+
|
|
223
|
+
- `createFastMCPBackend`
|
|
224
|
+
- Types: `BackendAdapter`, `BackendFactory`, `BackendSession`
|
|
225
|
+
|
|
226
|
+
## Scripts
|
|
227
|
+
|
|
228
|
+
```bash
|
|
229
|
+
pnpm validate # Pre-commit: format + lint + typecheck + test + build
|
|
230
|
+
pnpm test # Run tests
|
|
231
|
+
pnpm build # Build via tsdown
|
|
232
|
+
pnpm dev # Watch mode
|
|
233
|
+
pnpm typecheck # Type check only
|
|
234
|
+
```
|
|
235
|
+
|
|
236
|
+
## License
|
|
237
|
+
|
|
238
|
+
MIT © Jordan Burke
|
package/dist/index.d.ts
CHANGED
|
@@ -41,7 +41,13 @@ type SchemaParams = {
|
|
|
41
41
|
issues: ReadonlyArray<{
|
|
42
42
|
message: string;
|
|
43
43
|
}>;
|
|
44
|
-
}
|
|
44
|
+
} | Promise<{
|
|
45
|
+
value: unknown;
|
|
46
|
+
} | {
|
|
47
|
+
issues: ReadonlyArray<{
|
|
48
|
+
message: string;
|
|
49
|
+
}>;
|
|
50
|
+
}>;
|
|
45
51
|
};
|
|
46
52
|
};
|
|
47
53
|
type InferSchemaOutput<P extends SchemaParams> = NonNullable<P["~standard"]["types"]>["output"] extends infer O ? [O] extends [never] ? unknown : O : unknown;
|