maza
by prasanth263
README.md
# Maza
Maza is a local MCP gateway. Connect your MCP servers once, then point Claude Code or another MCP client at Maza. Agents see two tools: `find_tool` discovers a capability through Jev Choice, and `execute_tool` runs it after the agent supplies arguments from the original schema.
The gateway manages connections, credentials, discovery, per-tool access and execution. It runs as a native Node.js process; Docker is not required. Jev is the gateway's only model integration. Planning and argument generation remain with the calling agent.
## Requirements
- Node.js 22.13 or later and npm.
- An available OS credential store supported by `@napi-rs/keyring`. macOS Keychain has been tested. Linux/Windows credential-store integration has not been verified.
- A Jev API key from TypeSafe AI.
- Claude Code installed if you want automatic Claude registration.
- Each downstream MCP server's runtime and credentials, when required.
## Install and start locally
```sh
git clone https://github.com/prasanth263/maza.git
cd maza
npm ci
npm link
maza init
maza secret set jev
maza start
```
`npm link` makes the `maza` command available locally on your machine. If you prefer not to link it, replace `maza` with `node /absolute/path/to/maza/src/cli.js` in the commands below, or use `npm run maza -- <command>` from this repository.
`maza secret set jev` prompts for the key with hidden input. Paste the key and press Enter. The value is stored in OS credentials, not in the repository, shell arguments or configuration file. To replace the key later, run the same command; no daemon restart is needed solely for a key change. Missing or placeholder keys do not trigger a mock routing fallback.
By default, state is stored in `~/.maza`. To use another directory, set `MAZA_HOME` before running commands, or pass `--home /absolute/path/to/state` to every command, including client registration. Use one consistent directory: credential namespaces depend on its absolute path.
```sh
# Optional: use project-local state instead of ~/.maza.
export MAZA_HOME="$PWD/.maza"
```
The service listens at `http://127.0.0.1:47321`. Open [the local dashboard](http://127.0.0.1:47321/dashboard/). Obtain its administrator token with:
```sh
maza token admin
```
Paste that token into the dashboard's password field. The dashboard can manage connections, discover tools, enable tools, store credentials and inspect metadata-only activity.
## Add MCP servers through the CLI
### Local command / stdio
For example, connect the filesystem MCP server to one allowed directory:
```sh
mkdir -p "$HOME/maza-files"
maza add stdio files -- npx -y @modelcontextprotocol/server-filesystem "$HOME/maza-files"
maza discover files
maza tools files
maza enable files/read_text_file
maza enable files/list_directory
```
Everything after `--` is the downstream executable and its arguments. You can use an absolute executable path instead of `npx`, for example a Node script, Python executable or Docker command. Local MCP commands run as your OS user; Maza is not an OS sandbox.
### Remote Streamable HTTP
Replace the example URL with your provider's MCP endpoint:
```sh
maza add http service https://example.com/mcp
maza discover service
maza tools service
maza enable service/tool_name
```
Remote URLs must use HTTPS, except for local loopback HTTP. Put credentials in header references rather than URL userinfo or query parameters. Legacy SSE-only endpoints are not supported.
### Discovery and enablement
New tools are disabled until explicitly enabled. Tool IDs are namespaced as `server/tool_name` to avoid collisions.
```sh
maza discover # Refresh all enabled connections.
maza tools files # Inspect original definitions.
maza enable files/read_text_file
maza disable files/read_text_file
maza enable 'files/*' # Enable all currently discovered tools on files.
```
A changed tool definition requires renewed enablement. Selections bind to the original schema version and expire after five minutes. The gateway validates execution arguments and checks client access before proxying the call.
## Authentication: three separate credentials
| Connection | Credential | Where it is configured |
| --- | --- | --- |
| Gateway → Jev | Your Jev API key | `maza secret set jev` |
| Agent/dashboard → gateway | Gateway-issued bearer token | Created by `maza init`; retrieved with `maza token` |
| Gateway → downstream MCP | That provider's API key, environment secret or OAuth tokens | `maza secret set <reference>` plus a connection binding, or `maza login <server>` |
The Jev key is not a gateway login token. Downstream credentials are not passed to calling agents or included in routing payloads. Task/context and enabled tool names/descriptions are sent to Jev, so avoid putting secrets in those fields.
### Gateway authentication
`maza init` creates two random bearer credentials in OS storage:
- `admin`: access to the management API and dashboard.
- `agent`: MCP tool access to enabled tools, with no management access.
The nonsecret config stores credential references and token hashes. The service binds loopback, checks Host/Origin and verifies bearer tokens. Agent sessions have separate selection state and downstream protocol connections.
**Stdio clients:** run `maza bridge`. The bridge retrieves the `agent` credential from OS storage and authenticates to the shared service. You do not put a token into the MCP config.
**HTTP clients:** configure the endpoint `http://127.0.0.1:47321/mcp` with an `Authorization: Bearer <token>` header. Retrieve the agent token explicitly with:
```sh
maza token agent
```
Keep that token in your client's supported secret storage. The gateway uses local bearer authentication, not an inbound OAuth login flow. Clients that cannot configure a bearer header can use the stdio bridge. Do not expose this local service publicly.
### Restricted gateway clients
Stop the daemon before changing clients. Wait until `maza stop` has finished shutting down the process before starting it again.
```sh
maza stop
maza client add reader files/read_text_file files/list_directory
maza start
```
Configure that client to launch `maza bridge --client reader`, or retrieve its HTTP token using `maza token reader`. Both the client's allowlist and each tool's enablement must permit an action. Omitting tool IDs from `client add` grants access to all enabled tools.
To revoke a client:
```sh
maza stop
maza client revoke reader
maza start
```
Client changes require a restart. A selected tool is not user authorization: the calling host remains responsible for obtaining any required approval for an action.
### Downstream HTTP API keys
Store the complete header value using hidden input:
```sh
maza secret set service/bearer
```
At the prompt, enter the provider's complete header value, such as `Bearer YOUR_PROVIDER_TOKEN`. Create `service.local.json` with a reference, not the secret:
```json
{
"id": "service",
"type": "http",
"url": "https://example.com/mcp",
"headerRefs": {
"Authorization": "service/bearer"
}
}
```
Then add and discover it:
```sh
maza add json service.local.json
maza discover service
maza tools service
maza enable service/tool_name
```
For providers using `X-API-Key`, use that header name and store the raw API key as its referenced value. Choose a new connection ID if `service` already exists; onboarding does not overwrite existing entries.
### Downstream environment credentials
```sh
maza secret set provider/api-key
```
Create a connection file such as `provider.local.json`:
```json
{
"id": "provider",
"type": "stdio",
"command": "/absolute/path/to/node",
"args": ["/absolute/path/to/provider-mcp-server.js"],
"envRefs": {
"PROVIDER_API_KEY": "provider/api-key"
}
}
```
```sh
maza add json provider.local.json
maza discover provider
```
The server receives only configured credential bindings and a small base environment, not the gateway's entire environment. Literal `env` and `headers` are rejected by `add json`; use references. `env:VARIABLE_NAME` references are also supported, but the variable must exist in the daemon's environment when it starts. OS storage is simpler for the background service.
### Downstream OAuth
For a provider supporting MCP OAuth with a public client:
```sh
maza add http oauth-service https://example.com/mcp --oauth
maza login oauth-service
maza discover oauth-service
```
Open the authorization URL printed by `login`. The local callback uses `http://127.0.0.1:47322/callback`; the CLI waits up to three minutes. The SDK handles discovery, dynamic registration where supported, PKCE exchange and token refresh. Registration data and tokens are kept in OS storage.
For a pre-registered public client:
```sh
maza add http oauth-service https://example.com/mcp --oauth --client-id YOUR_CLIENT_ID
```
The provider must allow the callback URL. Provider-specific authentication, confidential-client secrets, device grants and nonstandard OAuth are not implemented. The adapter has protocol tests; real-provider OAuth interoperability has not yet been validated.
### Import an existing MCP config
```sh
maza stop
maza import /absolute/path/to/existing-mcp-config.json
maza start
maza discover
```
Import accepts a `mcpServers` map with local commands or HTTP URLs. Literal environment/header values are moved into OS storage, and only references are saved in Maza's config. Existing IDs are not overwritten, and discovered tools remain disabled until enabled. This does not support every host-specific config format.
## Connect Claude Code
Start Maza, then run registration from the project where you want to use it:
```sh
cd /path/to/your/project
maza register-claude
```
This invokes `claude mcp add --scope project` to add the stdio bridge. It preserves unrelated MCP entries and does not write bearer tokens into `.mcp.json`. Use the same `MAZA_HOME` or `--home` as the running service.
Ask Claude to perform a task using an enabled tool, for example reading a file inside the allowed filesystem directory. The intended flow is:
1. `find_tool` with the immediate task and relevant context.
2. Inspect the returned original schema and resolve any shortlist or no-match response.
3. Generate the tool's arguments and call `execute_tool` with the returned selection and tool IDs.
The host plans multi-step tasks and repeats this flow for each next action. Maza supplies usage instructions but cannot force a host to adopt them.
For other stdio MCP clients, use a configuration like:
```json
{
"mcpServers": {
"maza": {
"command": "/absolute/path/to/node",
"args": [
"/absolute/path/to/maza/src/cli.js",
"--home", "/absolute/path/to/maza-state",
"bridge"
]
}
}
}
```
## Service management and troubleshooting
```sh
maza status # Authenticated status, discovered tools and recent activity.
maza stop # Request graceful shutdown.
maza start # Start a detached background process.
maza serve # Run in the foreground instead.
npm test # Run the protocol and access-control tests from this repository.
```
Do not run foreground and background instances on the same port. To change the port, stop the service, update `port` in the state directory's `config.json`, then restart. CLI and bridge read that same config. `maza service-file` generates an optional macOS launchd plist; it does not install login autostart automatically.
| Symptom | Check |
| --- | --- |
| `CREDENTIAL_MISSING` | Set the required secret and confirm every command uses the same state directory. |
| `SERVICE_START_FAILED` | Inspect `service.log` in the state directory; check for a port conflict or unavailable credential store. |
| Discovery succeeds but `find_tool` finds nothing | Enable the relevant tools and check the client's allowlist. |
| `SCHEMA_CHANGED_REDISCOVER` | Refresh discovery, review and enable the changed tool, then call `find_tool` again. |
| `INVALID_OR_EXPIRED_SELECTION` | Rediscover in the same client session; selections expire and are single-use. |
| `OAUTH_LOGIN_REQUIRED` | Run `maza login SERVER_ID` and complete the provider's authorization flow. |
Transport failures and timeouts can occur after a downstream action has run. The gateway does not automatically retry execution; check the downstream state before retrying a mutation.
## Scope
The proxy exposes tools only; it does not forward downstream resource/prompt browsing, sampling, elicitation, roots or task extensions. It uses the official MCP SDK, SQLite for schema/cache metadata, AJV for argument validation, and OS credentials for secrets. Diagnostics omit request arguments, results and credential values.
Jev routing includes a none-apply option and returns close candidates for resolution. Large catalogs are evaluated in bounded stages without truncating author descriptions. Routing thresholds are experimental and require workload-specific calibration. Fewer agent-visible definitions do not guarantee faster execution or lower cost, especially when a host caches its full tool catalog.
The repository contains implementation code, tests and local setup instructions. Machine-specific state, client config, benchmark data, transcripts and credentials are excluded. `--test-stub` exists only for explicitly labeled development plumbing; normal operation always uses Jev.
This server cannot be deployed
Maintenance
ActivityMaintained
ResponsivenessNo issues