Skip to main content
Glama
README.md
# agent-factory-mcp

An MCP server that puts **The AI Agent Factory** curriculum — the Panaversity
*System of Record* — into any MCP client: Claude Code, Claude Desktop, Cursor,
Windsurf, or anything else that can spawn a stdio server.

```bash
npx -y @mj4384963/agent-factory-mcp --login
```

---

## Why this exists

The System of Record at `https://sor.panaversity.org/mcp` **already speaks MCP**
over Streamable HTTP. Normally that means you would point a client straight at
it and be done:

```bash
claude mcp add --transport http sor https://sor.panaversity.org/mcp   # does not work
```

That fails, and the reason is worth stating plainly. Generic remote MCP clients
obtain their OAuth `client_id` through **dynamic client registration**. This
authorization server does not offer it:

```console
$ curl -s https://auth.panaversity.org/.well-known/openid-configuration | jq 'has("registration_endpoint")'
false
```

With no `registration_endpoint` there is nothing for a generic client to
register against, so it never reaches a login screen. The `client_id` has to be
one that was **issued ahead of time**.

This package carries that pre-issued id, completes the authorization-code flow
with PKCE, holds the tokens, and forwards everything else straight through.

> **On shipping a client id in a public package.** The authorization server
> lists `none` under `token_endpoint_auth_methods_supported` — this is a public
> client, and a public client id is not a secret. There is **no client secret in
> this package**, and there must never be one. Your own browser login is the
> only thing that grants access, and the tokens it produces stay on your machine.

---

## Install

Nothing to install. Your MCP client runs it through `npx`.

### Claude Code

```bash
claude mcp add agent-factory -s user -- npx -y @mj4384963/agent-factory-mcp
```

### Claude Desktop / Cursor / Windsurf

```json
{
  "mcpServers": {
    "agent-factory": {
      "command": "npx",
      "args": ["-y", "@mj4384963/agent-factory-mcp"]
    }
  }
}
```

Then sign in once — the browser leg cannot happen inside a client's stdio pipe:

```bash
npx -y @mj4384963/agent-factory-mcp --login
```

Tokens land in `~/.agent-factory-mcp/tokens.json`, written `0600`. The scope
includes `offline_access`, so the refresh token keeps you signed in and you
should not have to repeat this.

---

## The tools

They are **not declared here**. `tools/list` is forwarded to the System of
Record and returned as-is, so whatever it advertises is what you get, and a tool
added upstream needs no release of this package.

As of writing, upstream advertises three:

| Tool | What it is for |
|---|---|
| `search_agent_factory` | A pointed question, answered by meaning (hybrid vector + keyword). `grain` selects `passage` / `section` / `lesson`. |
| `outline_agent_factory` | The map — sections and courses. Call with no argument to start, then with a `slug` to drill down one level. |
| `read_agent_factory_lesson` | One lesson by slug, byte-exact. Large lessons come back windowed; follow `next` to page through. |

Because it is a passthrough, that table is documentation, not a contract — the
live `tools/list` is always the truth.

---

## Commands

```bash
agent-factory-mcp            # run as an MCP server over stdio (what a client does)
agent-factory-mcp --login    # sign in once, in a browser
agent-factory-mcp --logout   # forget the saved tokens
agent-factory-mcp --doctor   # print the config and check the connection
```

`--doctor` is the first thing to run when something is wrong:

```console
$ npx -y @mj4384963/agent-factory-mcp --doctor

@mj4384963/agent-factory-mcp 1.0.0

  endpoint       https://sor.panaversity.org/mcp
  client_id      zia-tutor-ai
  redirect       http://localhost:8090/callback
  scope          openid profile email offline_access
  token file     ~/.agent-factory-mcp/tokens.json
  signed in      no  (run --login)
  endpoint says  HTTP 401  (expected without a token)
```

## Configuration

Everything has a working default; override only what you need.

| Variable | Default |
|---|---|
| `SOR_MCP_URL` | `https://sor.panaversity.org/mcp` |
| `SOR_CLIENT_ID` | `zia-tutor-ai` |
| `SOR_REDIRECT_URI` | `http://localhost:8090/callback` |
| `SOR_SCOPE` | `openid profile email offline_access` |
| `SOR_TOKEN_FILE` | `~/.agent-factory-mcp/tokens.json` |

---

## What is verified, and what is not

Stated up front rather than left to be discovered.

| | |
|---|---|
| The endpoint is an OAuth-protected MCP server | ✅ **Verified** — `401` with a `WWW-Authenticate` pointing at its resource metadata |
| The authorization server has no dynamic client registration | ✅ **Verified** — no `registration_endpoint` in its discovery document |
| `zia-tutor-ai` is a real client id | ✅ **Verified** — `/authorize` accepts it and fails only on the redirect, which is a later check |
| PKCE, state, and the `resource` parameter are built correctly | ✅ **Verified** — the generated authorize URL was inspected |
| Tool forwarding — list, call, schemas, error handling | ✅ **Verified** — 7 passing tests against a stand-in server (`npm test`) |
| **`http://localhost:8090/callback` is the registered redirect** | ⚠️ **Not verified.** It is a reasonable default, not a confirmed one. |
| An end-to-end sign-in against the live server | ⚠️ **Not performed** — it needs a real Panaversity account |

**If `--login` fails with `invalid_redirect`**, the default is simply not the
redirect this client id was registered with. Ask whoever issued `zia-tutor-ai`
for the exact string and set it:

```bash
SOR_REDIRECT_URI="http://localhost:1234/exact/registered/path" npx -y @mj4384963/agent-factory-mcp --login
```

The port in that URL is the port the local listener binds, so it always matches
what the server expects.

---

## Development

```bash
npm install
npm test        # spawns a stand-in System of Record and drives the bridge over stdio
npm run doctor
```

The tests deliberately avoid the real endpoint. Against it, a failure could be
the bridge or it could be the login, and those two are worth being able to tell
apart.

## License

MIT — see [LICENSE](LICENSE).

Built by [Muhammad Yaseen](https://github.com/my5757980) ·
[@MuhammadYa5968](https://x.com/MuhammadYa5968)