npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@aventara/mcp

v1.0.0-rc.12

Published

The MCP server for an Aventara application: its agent layer as Model Context Protocol tools.

Downloads

1,128

Readme

@aventara/mcp

The MCP server of an Aventara application. It turns the configuration's agent layer into Model Context Protocol tools, so an AI agent (Claude Desktop, Claude Code, any MCP client) can read and write the application's data under the same validation, exposure and pipelines as every other caller.

The server lives inside your application. @aventara/nest mounts it at the path the configuration's mcp key names, over the Streamable HTTP transport, stateless. You install this package beside @aventara/nest; you do not call it yourself.

npm install --save-exact @aventara/mcp@<your @aventara/core version>

It depends on @aventara/core and the official MCP TypeScript SDK (@modelcontextprotocol/sdk).

Turning it on

// src/aventara.config.ts
export async function aventaraConfig(prisma: PrismaService) {
	return {
		api: "/api",
		mcp: "/mcp",
		adapter: await createPrismaAdapter({ client: prisma, discovery, provider: "postgresql", driver: "@prisma/adapter-pg" }),
		// What the agent may reach: by default exactly what the client may.
		agent: {
			exposed: true,
			exemptions: { resources: { AuditLog: true } },
		},
	};
}

When the configuration has mcp, AventaraModule mounts the endpoint and logs it: Aventara mounted the MCP endpoint at /mcp (…). With mcp set and this package missing, the application refuses to start and names the package.

The tools

One tool per operation family the agent layer exposes, and none for a family nothing exposes:

| Tool | Input | Hints | |---|---|---| | find | { resource, variant, args? } | read-only | | create | { resource, variant, args } | not destructive | | update | { resource, variant, args } | destructive | | delete | { resource, variant, args } | destructive | | upsert | { resource, variant, args } | destructive | | transaction | { steps: [{ resource, family, variant, args? }] } | destructive | | me | {} | read-only |

resource and variant are enums read from the agent layer, so a resource the agent layer hides is not offered (and is refused with A2001 if an agent names it anyway). args is the operation's arguments as the protocol defines them (where, select, data, …). transaction is offered when the agent layer's transactions are interactive. A step can use a value an earlier step returned with { "$ref": { "operation": 0, "path": ["id"] } }; the server adds the fingerprints the protocol requires. me is offered when the framework's access provider states its client: it answers the identity Aventara Access resolves from the request's credentials ({ sub, claims }, or null) as the envelope's data, holding only what the access section's me schema declares (an identity the schema does not describe is the server's own failure, A3000). There is no sign-in tool.

Every call runs as the agent origin, through the same decoding and validation as an HTTP call, with the agent layer's pipelines. A success answers the protocol's envelope ({ data, code, cause }) as structured content and as text. A failure answers an MCP tool error (isError: true) carrying the envelope, its cause and its issues (path, code, check, params, message), so the agent can fix its arguments and call again.

The contract resource

The server exposes the Agent Contract as the MCP resource aventara://contract: every resource, its fields and their types, what can be filtered, selected, created and updated, and the request limits. The tool descriptions and the server's instructions tell the agent to read it first.

Write safety

What an agent can do is decided where everything else is: exposure (the agent layer, which can only narrow the layer it extends) and guards. A guard can tell an agent's call by its origin:

const humansOnly: AvGuard = (ctx) => ctx.$origin !== "agent";

The tool hints (readOnlyHint, destructiveHint) are hints for the client; they enforce nothing.

Authorization

With mcp.authorization, the endpoint is an OAuth 2.1 protected resource. It never issues tokens: your authorization server does.

import { createRemoteJWKSet, jwtVerify } from "jose";

const jwks = createRemoteJWKSet(new URL("https://auth.example.com/.well-known/jwks.json"));

mcp: {
	path: "/mcp",
	authorization: {
		servers: ["https://auth.example.com"],
		verify: async (token) => {
			try {
				const { payload } = await jwtVerify(token, jwks, {
					issuer: "https://auth.example.com",
					audience: "https://api.example.com/mcp",
				});
				return payload;
			} catch {
				return null;
			}
		},
	},
},

Each server is an https URL. One on your own machine may be http instead, so a local setup needs no TLS: http://127.0.0.1:4000 or http://[::1]:4000, with any port and path. That is the whole list. Use the address, not localhost: a name is looked up, and localhost can resolve somewhere else (RFC 8252, section 8.3), so http://localhost:4000 is refused with the spelling to use, http://127.0.0.1:4000. 0.0.0.0, [::], other 127.x addresses, IPv4-mapped addresses, names such as auth.localhost and URLs with credentials are refused like any other http URL. Write the URL the way a browser would show it: http://127.1:4000 is refused, and the error gives the spelling to use. No server URL, https included, may have a query or fragment (an issuer has neither), an empty path segment (//) or port 0.

An http authorization server is for local development only. Tokens and authorization codes cross it unencrypted, and only clients on the same machine can reach it. Deploy with an https server.

  • GET /.well-known/oauth-protected-resource/mcp answers the protected-resource metadata (RFC 9728): the endpoint's URL as resource, your servers as authorization_servers, and bearer_methods_supported: ["header"].
  • A request without Authorization: Bearer <token> is 401 with WWW-Authenticate: Bearer resource_metadata="…" naming that metadata, which is how an MCP client discovers where to sign in. As RFC 6750 asks, it carries no error code: the request had no token to be wrong.
  • Each token goes to verify. null (or undefined, or a throw) is 401 with error="invalid_token". A verify that throws is also reported to your diagnostics sink (an AvMcpDiagnostic holding the error), so an outage of your token check shows in your logs rather than as a run of rejected agents. Anything else lets the request in, and what verify returned is the request's ctx.$state.mcp.claims, beside what the host's state option seeds. Declare its type with the rest of your state:
declare module "@aventara/core" {
	interface AvState {
		mcp?: { claims: JWTPayload };
	}
}

The endpoint's URL comes from the request as your platform reports it, so behind a proxy set Express's trust proxy or Fastify's trustProxy for https and the public host to appear in the metadata.

Without authorization

An endpoint without authorization takes no token, so it answers callers on the same machine only: a loopback peer address, no proxy forwarding header (Forwarded, X-Forwarded-For, X-Real-IP), a loopback Host (localhost, 127.x.x.x or [::1], any port), and no Origin header but a loopback one. Every other caller gets 403. The Host rule closes DNS rebinding: a page whose domain is rebound to 127.0.0.1 still sends its own domain as Host. An HTTP/2 caller sends :authority instead of Host, so the open endpoint refuses it too; serve it over HTTP/1.1, or configure authorization. This is for local development and for an agent running beside the application. To serve remote agents, configure authorization.

The transport

Streamable HTTP, stateless: every POST is answered on its own with a JSON response, and no session is kept. GET and DELETE answer 405. A request body is limited to the agent layer's maxRequestBytes.

License

PolyForm Shield 1.0.0 with an additional permission. It is free to use, including in commercial applications, but you may not use it to build a product that competes with Aventara itself. See LICENSE and LICENSE-ADDITIONAL-PERMISSION.md.