# auth.md — agent registration for Produlis

Produlis exposes one authenticated capability: filing a project brief. It is
reachable two ways — as the `submit_project_brief` tool on the MCP server at
https://produlis.com/mcp, and as the `produlis-project-brief` skill on the A2A agent
at https://produlis.com/a2a/v1. One token works for both. Everything else on https://produlis.com is
public and needs no credentials.

## Who this is for

Autonomous agents acting for a person who has asked them to contact Produlis.
There are no Produlis user accounts involved — identity here is **anonymous**
and scoped to the client software, not to a person.

## Discovery

| Document | URL |
| --- | --- |
| Protected resource metadata, MCP endpoint (RFC 9728) | https://produlis.com/.well-known/oauth-protected-resource/mcp |
| Protected resource metadata, A2A endpoint (RFC 9728) | https://produlis.com/.well-known/oauth-protected-resource/a2a/v1 |
| Protected resource metadata, origin | https://produlis.com/.well-known/oauth-protected-resource |
| Authorization server metadata (RFC 8414) | https://produlis.com/.well-known/oauth-authorization-server |
| JSON Web Key Set | https://produlis.com/.well-known/jwks.json |
| MCP server card | https://produlis.com/.well-known/mcp/server-card.json |
| A2A agent card | https://produlis.com/.well-known/agent-card.json |

## Register

`POST https://produlis.com/oauth/register` — RFC 7591 dynamic client registration, open to
anyone, no approval step.

```http
POST /oauth/register HTTP/1.1
Host: produlis.com
Content-Type: application/json

{
  "client_name": "Your agent",
  "redirect_uris": ["http://127.0.0.1:8976/callback"],
  "grant_types": ["authorization_code", "client_credentials"],
  "token_endpoint_auth_method": "client_secret_post"
}
```

The response carries `client_id`, and `client_secret` unless you registered
with `"token_endpoint_auth_method": "none"`. Registrations do not expire.

## Get a token

**Machine to machine** — no browser, no human:

```http
POST /oauth/token HTTP/1.1
Host: produlis.com
Content-Type: application/x-www-form-urlencoded

grant_type=client_credentials&client_id=…&client_secret=…&scope=produlis:brief&resource=https://produlis.com/mcp
```

**Human in the loop** — `authorization_code` with PKCE (`S256` required).
Send the user to https://produlis.com/oauth/authorize; they see one consent screen naming
your client and the scopes, and the code comes back to your redirect URI.

## Scopes

| Scope | Grants |
| --- | --- |
| `produlis:read` | Read public information about Produlis. Always granted. |
| `produlis:brief` | Call `submit_project_brief` (MCP) or `produlis-project-brief` (A2A), which create a real CRM lead. |

## Using the token

Bearer token in the `Authorization` header:

```http
POST /mcp HTTP/1.1
Host: produlis.com
Authorization: Bearer <access_token>
Content-Type: application/json

{"jsonrpc":"2.0","id":1,"method":"tools/list"}
```

Tokens are ES256 JWTs valid for one hour, verifiable offline against the JWKS
above. Request `resource=https://produlis.com/mcp` or `resource=https://produlis.com/a2a/v1`;
a token for either endpoint, or for the origin, is accepted at both. A rejected
token comes back `401` with
`WWW-Authenticate: Bearer resource_metadata="https://produlis.com/.well-known/oauth-protected-resource/mcp"`.

## Revocation

There is no revocation endpoint. Tokens are short-lived and stateless; stop
using a client and its tokens expire within the hour. To report abuse of a
client, email hello@produlis.com.

## Etiquette

`submit_project_brief` reaches a human inbox. Call it only when the person you
are acting for has asked you to contact Produlis and has confirmed their name,
company and email.
