The agent layer
Act with permission. Leave a trail.
PAVE is built so people can work through agents, safely. Borrowers and approved brokers can connect any AI assistant they trust — to read, write, chase, submit, and record — inside the same permissions and audit trail they already have.
Two clicks. Real OAuth. Full power.
Paste
Paste your PAVE address — https://pave.legendhasit.co.nz/mcp/borrower for borrowers, https://pave.legendhasit.co.nz/mcp/broker for approved brokers — into the AI assistant of your choice.
Discover
The client makes one unauthenticated request and follows the OAuth flow your assistant already knows — no setup, no copy-pasting tokens.
Authorise
You're redirected here, signed in, and shown a single Authorize button on PAVE's branded consent screen. Click it.
Talk
Talk to your assistant. It reads, writes, and acts on your behalf. Every move shows up in Settings → AI agents. Revoke any time.
Your agent can act for you — but never beyond you.
Read, decide, respond.
- Confirm identity (
WhoAmI) and find out who your broker is (GetMyBroker). - List and read your applications, see status, progress, offers.
- Update your borrower profile (phone, address, preferred name).
- Accept or decline an offer the bank has presented.
- Leave notes that you and your broker can both see.
Lead, submit, record.
- Confirm identity and broker approval status (
WhoAmI,GetMyBrokerProfile). - Triage your pipeline (
ListMyBorrowers,ListBrokerApplications,GetBrokerApplication,GetBorrower). - Update applications and request information from a borrower.
- Submit applications to banks; record offers as they arrive.
- Leave notes that the borrower and broker can both see.
Beyond your authority.
- Reach data that belongs to another user — every tool re-authorises.
- Change your role, status, or password.
- Bypass the broker-approval gate.
- Avoid the audit log.
Built so you can let an agent loose, then watch.
Real authorization-code-with-PKCE flow. Dynamic client registration, branded consent. No copy-pasting tokens.
Access tokens last 30 days. Refresh tokens last 90. A leaked token has a finite blast radius.
Every successful tool call is recorded with the actor, timestamp, IP, user agent, OAuth client, and metadata.
Email the moment a new agent is authorised — same pattern as "you logged in from a new device".
Email when the agent accepts an offer, declines an offer, or submits an application to a bank.
Per-token revoke from Settings → AI agents. Master kill switch disables all agent access.
120 requests per minute per token. A runaway client cannot starve the platform.
Route middleware (auth, role, mcp.enabled, broker-approval, throttle) plus per-record Eloquent policies inside every tool.
Technical reference. Spec-compliant. No surprises.
Everything below is the proof under the pitch — endpoints, OAuth flow, RFC references, MCP versions, worked examples, and tool notes for the people building agents on PAVE.
| Audience | URL | Auth |
|---|---|---|
| Borrower | https://pave.legendhasit.co.nz/mcp/borrower |
Bearer (borrower token) |
| Broker | https://pave.legendhasit.co.nz/mcp/broker |
Bearer (approved broker token) |
| DCR | https://pave.legendhasit.co.nz/oauth/register |
Open (RFC 7591) |
Single OAuth scope: mcp:use. Borrower-vs-broker separation is the URL plus role middleware. A borrower's token gets 403 at /mcp/broker, and vice versa.
The server advertises support for MCP protocol versions 2025-11-25,
2025-06-18, 2025-03-26, and 2024-11-05.
It picks the latest the client requests during initialize.
# 1. Unauthenticated MCP call → 401 with discovery pointer
curl -i -X POST https://pave.legendhasit.co.nz/mcp/borrower \
-H 'Content-Type: application/json' \
-d '{"jsonrpc":"2.0","id":1,"method":"tools/list"}'
HTTP/1.1 401 Unauthorized
WWW-Authenticate: Bearer realm="mcp",
resource_metadata="https://pave.legendhasit.co.nz/.well-known/oauth-protected-resource/mcp/borrower"
# 2. Discover the protected-resource metadata, then the auth server
curl -s https://pave.legendhasit.co.nz/.well-known/oauth-protected-resource/mcp/borrower
# → { "resource":"…", "authorization_servers":["https://pave.legendhasit.co.nz"], "scopes_supported":["mcp:use"] }
curl -s https://pave.legendhasit.co.nz/.well-known/oauth-authorization-server
# → { "issuer":"…", "authorization_endpoint":"https://pave.legendhasit.co.nz/oauth/authorize",
# "token_endpoint":"https://pave.legendhasit.co.nz/oauth/token",
# "registration_endpoint":"https://pave.legendhasit.co.nz/oauth/register",
# "code_challenge_methods_supported":["S256"],
# "scopes_supported":["mcp:use"],
# "grant_types_supported":["authorization_code","refresh_token"] }
# 3. Dynamically register your client
curl -s -X POST https://pave.legendhasit.co.nz/oauth/register \
-H 'Content-Type: application/json' \
-d '{
"client_name": "Your assistant",
"redirect_uris": ["https://your-client.example/oauth/callback"]
}'
# → { "client_id":"…", "grant_types":["authorization_code","refresh_token"],
# "redirect_uris":["…"], "scope":"mcp:use",
# "token_endpoint_auth_method":"none" }
# 4. PKCE authorization-code flow
# 4a. Open the authorize URL in the user's browser:
# https://pave.legendhasit.co.nz/oauth/authorize
# ?response_type=code
# &client_id=…
# &redirect_uri=https%3A%2F%2Fyour-client.example%2Foauth%2Fcallback
# &scope=mcp%3Ause
# &code_challenge=…
# &code_challenge_method=S256
# &state=…
# 4b. The user lands on the branded "Authorize" screen. After click, we
# redirect to redirect_uri with ?code=…&state=…
# 4c. Exchange the code:
curl -s -X POST https://pave.legendhasit.co.nz/oauth/token \
-H 'Content-Type: application/x-www-form-urlencoded' \
-d 'grant_type=authorization_code&client_id=…&code=…&code_verifier=…&redirect_uri=https%3A%2F%2Fyour-client.example%2Foauth%2Fcallback'
# → { "token_type":"Bearer", "expires_in":2592000,
# "access_token":"eyJ…", "refresh_token":"…" }
# 5. Use the bearer token
curl -s -X POST https://pave.legendhasit.co.nz/mcp/borrower \
-H 'Authorization: Bearer eyJ…' \
-H 'Content-Type: application/json' \
-d '{"jsonrpc":"2.0","id":1,"method":"tools/list"}'
Desktop clients that use a custom-scheme callback (your-app://oauth/callback)
work too — schemes are configured server-side per-deployment.
# Request
{
"jsonrpc": "2.0",
"id": 2,
"method": "tools/call",
"params": {
"name": "ListMyApplications",
"arguments": {"limit": 5}
}
}
# Response (success)
{
"jsonrpc": "2.0",
"id": 2,
"result": {
"content": [{
"type": "text",
"text": "{\"count\":1,\"applications\":[{\"id\":42,\"reference\":\"MTG-AB12CD\",\"status\":\"submitted_to_broker\",\"loan_amount_requested\":\"750000.00\",…}],\"next_after_id\":null}"
}],
"isError": false
}
}
# Response (tool-level error — authorisation, validation, missing record)
{
"jsonrpc": "2.0",
"id": 2,
"result": {
"content": [{ "type":"text", "text":"This action is unauthorized." }],
"isError": true
}
}
Every JSON tool returns its payload as a single text content
item containing JSON-encoded data. Parse the inner string. Tool-level
isError: true means a controlled failure (auth, validation,
not-found); do not retry on those.
# List available resources
{ "jsonrpc":"2.0", "id":3, "method":"resources/list" }
# → result.resources contains entries like:
# {
# "name": "my-profile-resource",
# "title": "My Profile Resource",
# "uri": "pave://borrower/me",
# "mimeType": "application/json",
# "description": "The authenticated borrower's profile…"
# }
# Read one
{
"jsonrpc": "2.0",
"id": 4,
"method": "resources/read",
"params": { "uri": "pave://borrower/me" }
}
| URI | Server | What it returns |
|---|---|---|
pave://borrower/me |
Borrower | The authenticated borrower's profile snapshot. |
pave://borrower/banks |
Borrower | Active NZ banks PAVE works with (id, name, slug). |
pave://broker/banks |
Broker | Same plus contact email — pass id to SubmitToBank. |
| Name | Server | What it guides |
|---|---|---|
StartApplicationPrompt |
Borrower | An eight-step interview that collects everything a broker needs to start an application. |
PipelineSummaryPrompt |
Broker | A weekly pipeline review: bucket by status, surface what needs the broker's action. |
Use prompts/list and prompts/get to fetch them. The body of each
is a system-prompt-style instruction the agent can adopt for the workflow.
List tools (ListMyApplications, ListBrokerApplications) accept limit and after_id. Every response includes a next_after_id — pass it back to fetch the next page; null means end-of-list.
Authorisation and validation failures return a tool-level error: {"isError": true, "content": [{"type":"text","text":"…"}]}. Don't retry on auth errors — surface the message to the user. Server errors come back as JSON-RPC error code -32603.
- Application creation is broker-led. Borrowers can edit applications in
draftorneeds_borrower_infoviaUpdateMyProfile+AddNotefor context, but creating the application record happens through their broker. TheStartApplicationPromptguides agents to collect detail and hand it to the broker via a note. - Notes have no visibility flag. Every note on an application is visible to both the borrower and the assigned broker. Phrase accordingly.
- File uploads aren't supported via MCP yet. Borrowers and brokers upload supporting documents through the web UI (Settings or the application page).
- Streaming responses aren't used. Every tool returns a single response object; no
notificationsare emitted while a tool runs. RecordOfferuses decimal interest rates.0.0625is 6.25%, not6.25. The schema rejects values above 1.
- Your authority is scoped to the user who authorised you. Don't try to access another user's data — the server will return errors.
- Tool descriptions are authoritative. Read each tool's description and input schema before calling.
- Prefer reading first.
WhoAmIfollowed by a list tool gives you full context before you make any changes. - Never guess identifiers. References (
MTG-XXXXXX), offer ids, bank ids, submission ids — they all come from list/get tools or resources. - Significant actions trigger an email to the user. They will know immediately if you act unexpectedly.
Start the application. Let your agent keep the thread.
Once you're signed up, paste your borrower URL into the assistant of your choice and ask it where you're up to.
Begin your application →See PAVE in motion. Run it through your agent.
Thirty-minute walkthrough. Broker workspace, borrower journey, agent layer.