# RetailHardHat MCP

RetailHardHat exposes a remote MCP server at **`/api/mcp`**, using stateless
Streamable HTTP with JSON responses. It supports tools, resources, prompts, and
MCP protocol initialization through the official TypeScript SDK.

Start with [Developer documentation](mcp/README.md), also served publicly at
`/api/mcp-public/docs`, for the full reference, runnable client and extension guide.

## Connect an AI provider using OAuth

Use **https://app.retailhardhat.com/api/mcp** as the remote Streamable HTTP URL
after publishing this version. Supported clients discover the OAuth server,
register a client, and open the RetailHardHat consent page. Sign in through the
Admin portal as an organization owner/admin, verify the client and callback
origin, then select the modules and read-only or guarded-write access.

OAuth uses PKCE S256, one-hour access tokens, rotating refresh tokens, and a
maximum 90-day grant. Refresh can narrow permissions, never expand them.
Reusing a consumed refresh token revokes the grant. Required in-app review binds
immutable arguments; configurable rules may waive extra MCP review, never
underlying business approval.
Revoke access in **Settings → MCP → Connections**.

See [provider setup and submission](mcp-providers/README.md) for discovery
addresses, package generation, tests, and the external publisher/review steps.
The **Providers** tab distinguishes local configuration from directory
acceptance. A real Microsoft token-store registration is required to finish
the Copilot sideload ZIP; templates are not a substitute.

## Connect a legacy bearer-header client

1. Sign in as an organization owner or administrator.
2. Open **Settings → MCP → New connection**.
3. Choose the modules, read-only or guarded-write access, and expiry (1–90 days).
4. Copy the token when it is displayed. It is shown once and cannot be recovered.
5. Open **Setup** and copy the configuration for Cursor, VS Code, or Claude Code.
   Use the URL of the environment you intend to connect to.

Example for clients using `mcpServers`:

```json
{
  "mcpServers": {
    "retailhardhat": {
      "url": "https://YOUR_APP_HOST/api/mcp",
      "headers": {
        "Authorization": "Bearer YOUR_MCP_TOKEN"
      }
    }
  }
}
```

VS Code uses a `servers` object rather than `mcpServers`, with `"type": "http"`.
Claude Code accepts:

```sh
claude mcp add --transport http retailhardhat https://YOUR_APP_HOST/api/mcp --header "Authorization: Bearer YOUR_MCP_TOKEN"
```

Treat a connection token as a password. Store it in your AI client's supported
secret mechanism where available. Never put a real token in a repository,
shared configuration, prompt, screenshot, or support message.

The manual-token flow remains supported alongside browser OAuth. No legacy SSE
subscription or outbound MCP-client functionality is provided.

## Supported modules

Sites, deals, tasks, projects, whole budgets and budget categories, vendors,
permits, bids, RFIs, RFPs, contacts, construction invoices, change requests and
their financial line items, purchase lists/templates/tracking, documents and
folders, synchronized leases, field operations, and organization reports.
Field operations include daily logs/photos, punch lists, utilities and closeout
document records. The **Tools** tab is the live catalogue. The MCP
`tools/list` response only includes the modules and write access granted to that
connection. Entity operations use the existing application's API validation,
business rules, and tenant restrictions. Identity, credential, platform-admin,
direct payment and arbitrary SQL/HTTP operations are deliberately not exposed.

Paged list tools support `limit` (1–100), `offset`, optional text `search`, and
applicable site/status/project filters. Array responses become
`{ items, total, limit, offset, nextOffset }`; endpoint-specific summaries retain
their application response shape. Use `nextOffset` to retrieve the next page.
Monetary values are decimal strings without currency symbols; date fields accept
ISO calendar dates or date-time strings according to the tool schema.

Existing connections retain their original scopes. Create a new connection with
the additional modules selected to make these tools available; publishing does
not silently expand an existing token's permissions.

### File workflow

1. Call `request_file_upload` with the MIME type and exact byte length (maximum
   50 MiB). Both policies must allow document sharing. If current rules require
   review, approve and execute the request.
2. PUT the bytes directly to the returned `uploadURL`, with the matching
   `Content-Type`. This signed URL expires in 15 minutes; keep it private.
3. Pass the returned `key` to the appropriate `attach_*_document` tool (or
   `attach_daily_log_photo`). Each write follows its current approval rule.
   An unclaimed upload must originate from the same MCP connection and be
   attached within one hour. Existing organization-owned files may be reused.
4. Use `read_*_document` to retrieve text or base64 file bytes, up to 2 MiB.
   Larger files should be downloaded in the app. Invoice attachments also have
   an existing short-lived `get_invoice_download_url` tool.

File paths are managed App Storage keys, not arbitrary URLs. A tool cannot claim
another organization's files, change storage identity through a metadata edit,
or attach an unknown/unclaimed key. Upload URLs and file bytes are not persisted
in MCP activity. Document and folder deletion follows the existing app's rules,
including its cascading behavior; do not assume every module physically removes
the underlying file.

### Financial and lease boundaries

Budget edits/deletions do not offer force, approval or lock overrides. Invoice
tools create/edit business records but do not move money or mark invoices paid.
Change-request tools do not self-approve requests or set financial-impact-applied
flags. These workflows still run through the app's ordinary business controls.
LeaseCommand remains authoritative for lease contents: MCP reads cached leases
and activity, checks connection status, and can request an approved refresh using
an already-configured integration. It does not create source leases or manage
integration credentials.

Resources: `retailhardhat://connection` and `retailhardhat://tools`.
Prompts: `operations_review` and `budget_review`.

## Approval lifecycle

- Ordinary task creation/editing runs immediately by default for guarded-write connections.
- Sensitive writes require approval by default. Organization/connection rules may
  require or waive extra MCP review by tool, action or sensitivity family.
  Existing business approvals remain enforced.
- Writes requiring review return **`pending_approval`**.
  No requested business change has happened yet.
- A human opens **Settings → MCP → Approvals**, checks the exact tool and
  arguments, and explicitly approves or rejects. Approval does not execute.
- The same AI connection polls `get_approval_status`, then calls
  `execute_approved_action` with the approval ID.
- Arguments come from the stored request, not from the execution call.
  Approvals expire after the configured interval (default/maximum 24 hours), are connection/tenant-bound, and can be
  consumed once. Concurrent execution attempts cannot use the same approval.
- Rejected, expired, used, and revoked-connection approvals cannot execute.
- Policy updates invalidate pending and approved requests.
- If execution fails or the outcome is uncertain, inspect application records
  before requesting a new approval. Never automatically retry mutations.
  An `executing` request left after a process interruption is intentionally not
  reusable; check its activity and underlying records.
- Deletions may cascade under the app's normal rules. Review them carefully.

## Administration and security

Connections act as the administrator who created them, never as a platform
superadmin. The creator's current role and suspension state are checked on every
MCP request. Removing their account or administrator role disables access.
Switching the creator's browser organization does not change a token's tenant.

Only SHA-256 hashes of high-entropy tokens are stored. Tokens have expiry and
can be revoked immediately. MCP never accepts a browser cookie instead of a
bearer token. Session-based management endpoints still require CSRF protection.
Existing CORS restrictions remain in place.

Actual operations run with short-lived, server-side delegated sessions through
the existing API, on the local server only. These session credentials are never
returned to clients. Foreign-key organization checks add protection for legacy
routes. Secret-like response fields are removed recursively.

Requests are subject to the app's global rate limiter and persistent
organization/user/connection limits. Default connection budgets are 120
requests/minute, four concurrent client operations, 100 changes/hour and 50 MiB
download accounting/hour. Organization policy imposes ceilings. Response caps
default to 2 MiB; API execution has a 25-second timeout. Narrow an oversized query
rather than assuming it returned complete results.

Settings → MCP → Security manages module/action/tool/record restrictions,
approval rules, document/export grants, field masks, quotas, approved actual OAuth
registration IDs, security activity and in-app alerts. Unsupported record scopes
fail closed. Connection document/export grants default off. Masks redact JSON
fields, not attachment contents. Emergency shutdown permanently revokes old
credentials; re-enabling does not restore them. See [Security reference](mcp/security.md).

Connections, approvals and activity are persistent PostgreSQL records installed
by the app's normal SQL migrations. The Settings view shows all active
connections and unexpired open requests, plus the latest 200 connections/requests
and 100 activity entries. Older activity remains in the database.
No secrets or business payload results are stored in activity rows.
Exact action arguments are stored only on approval requests for human review.

## Verification

See [Testing and release checks](mcp/testing.md) for protocol, OAuth, security,
native-view, documentation-client and browser suites.

Only explicit development startup applies SQL migrations. Production and
publish-test startup perform read-only checks; Publish owns production schema
changes. After publishing,
create a separate connection in the live organization; do not reuse development
tokens against production.
