KeryxCAUTION
Keryx: The Fullstack TypeScript Framework for MCP and APIs
Overview
From the repository's own README, as read at the audited commit. Badges and raw HTML are left out.
The fullstack TypeScript framework for MCP and APIs.
[](https://github.com/actionhero/keryx/actions/workflows/test.yaml)
What is this Project?
The big idea behind Keryx: write your controller once, and it works everywhere. A single action class handles HTTP requests, WebSocket messages, CLI commands, background tasks, and MCP tool calls — same inputs, same Zod validation, same middleware, same response. You write the logic once; the only thing that changes is how the request arrived and how the response gets delivered.
That last transport — MCP — is the reason I built this now. Every action is automatically an MCP tool, so AI agents authenticate with built-in OAuth 2.1, get typed errors, and call the exact same validated endpoints your HTTP clients do. There's no second MCP server to maintain and no schemas to keep in sync... you expose your app to agents by writing the app.
And it's a real backend, not a toy. You get Drizzle ORM with auto-migrations, background tasks with fan-out on Resque, streaming responses (SSE, chunked binary, and the same stream over WebSocket and MCP), OpenAPI generation, pagination and caching helpers, database transactions, and a companion Vite + React frontend that reads your actions with end-to-end type safety — no code generation. It's all on Bun, so TypeScript runs natively and startup is fast.
One Action, Every Transport
Here's what that looks like in practice. This is one action:
export class UserCreate implements Action {
name = "user:create";
description = "Create a new user";
inputs = z.object({
name: z.string().min(3),
email: z.string().email(),
password: secret(z.string().min(8)),
});
web = 0820c51d8103OBSERVED · 2026-10-08Connect
Built from this server's own package name, version and transport as found in its source — not copied from anyone's documentation, so it cannot drift against a page we do not control. Replace the environment placeholders with a token scoped to the least it needs.
claude mcp add tracing --env RESQUE_ADMIN_PASSWORD=${RESQUE_ADMIN_PASSWORD} -- npx -y @keryxjs/[email protected]{
"mcpServers": {
"tracing": {
"command": "npx",
"args": [
"-y",
"@keryxjs/[email protected]"
],
"env": {
"RESQUE_ADMIN_PASSWORD": "${RESQUE_ADMIN_PASSWORD}"
}
}
}
}Exposed tools (4)
4 read · 0 write · 0 destructive.
| Tool | Risk | Description |
|---|---|---|
messages | read | Authenticated channel for real-time messages |
plugin-channel | read | A channel from a plugin |
pluginAction | read | An action from a plugin |
x | read | Does the X thing |
Trust audit
CAUTIONgrade D · trust 61/100 Install with care. The audit found things worth knowing before you trust its output.
| Layer | What it checks | Result |
|---|---|---|
| L0 | Provenance & inventory | PASS |
| L1 | Static analysis of the code | WARN |
| L2 | Instruction surface (what it tells the agent) | PASS |
| L3 | Class-specific surface | PASS |
| L4 | Behavioural (sandbox) | SKIPPED |
What the source does
- Filesystem
- none-observed
- Network
- declared (1 observation(s))
- Shell
- none-observed
- Dependencies
- not all pinned
- Secrets in source
- found
Findings (25)
DATABASE_URL_TEST: postgres://postgres:postgres@localhost:5432/keryx-test
DATABASE_URL_TEST: postgres://postgres:postgres@localhost:5432/keryx-test
DATABASE_URL_TEST: postgres://postgres:postgres@localhost:5432/keryx-test
DATABASE_URL_TEST: postgres://postgres:postgres@localhost:5432/keryx-test
DATABASE_URL_TEST: postgres://postgres:postgres@localhost:5432/keryx-test
const secret = "s3cret-parameter-value";
.prettierignore
.index.ts
taskFrequency = eval(freqMatch[1].trim());
expect(() => new Function(script)).not.toThrow();
import type { ChannelMembers } from "../../actions/channel";import type { SessionCreate } from "../../actions/session";import type { FileUpload } from "../../actions/files";import type { GreetingPrompt, StatusResource } from "../../actions/mcp";import { MessagesCleanup, MessagesHello } from "../../actions/message";- The URL resolves to a publicly routable address. Loopback, private, link-local (including cloud instance-metadata endpoints such as `169.254.169.254`), CGNAT, and multicast targets are refused, and
- **SSRF guard** — the host must resolve to a publicly routable address. Loopback, private (RFC 1918), link-local (including `169.254.169.254`, the cloud instance-metadata endpoint), CGNAT, benchmarki
"https://169.254.169.254/latest/meta-data.json",
redirect_uris: ["http://127.0.0.1:3000/callback"],
"https://127.0.0.1/client.json",
"https://10.1.2.3/client.json",
"https://192.168.1.1/client.json",
"https://172.16.0.1/client.json",
decoded = atob(header.slice(6).trim());
vitepress, vue, vitepress-plugin-llms, ts-morph
Gates applied: no_behavioural_pass.
0820c51d8103full audit observations/trust-audit/mcp-server/actionhero__keryx.json · Report an issue / request a re-scanAudit history
Every audit this server has had. A grade with a past is a grade somebody is still checking.
| Date | Source | Verdict | Grade | Score | Change |
|---|---|---|---|---|---|
| 2026-10-08 | 0820c51d8103 | CAUTION | D | 61 | first audit |
Questions
What is the Keryx MCP server?
Keryx: The Fullstack TypeScript Framework for MCP and APIs
What tools does Keryx expose?
4 in total: 4 read-only, 0 that write, and 0 that can delete or overwrite. Every one is listed on this page with its risk.
Is Keryx safe to connect to an agent?
With care. The audit graded it D (61/100) and found 25 things worth knowing before you trust this server, listed below with the exact line each was found on.
What credentials does Keryx need?
It reads RESQUE_ADMIN_PASSWORD from the environment. Give it a token scoped to the least it needs — an agent that can be talked into calling a tool can be talked into calling it with your credentials.
How does Keryx run?
It speaks streamable-http, so it runs as a service you connect to over the network. It is published on npm as @keryxjs/tracing at 0.4.1.
How current is this page?
The grade is for one exact copy of the source (0820c51d8103), read on 2026-10-08. The repository is watched and re-audited when it changes.