Docs / API and agents

MCP server for agents

Connect Claude, ChatGPT or Cursor to VendorLogic over remote MCP with OAuth: who may use it, the tools per role, staff proof, audit attribution, rate limits and errors.

VendorLogic is a remote MCP server at https://<host>/v1/mcp (Streamable HTTP, stateless, JSON responses). It lets a signed-in credit-team member work with their own organization's applications from an agent. Code: apps/services/src/mcp.

Who can use it#

Every active person with a linked WorkOS identity and a role that holds mcp.use: supplier roles (in an APPROVED supplier organization), customer roles (Owner, Administrator, Buyer manager, Buyer; an organization is optional) and VendorLogic staff (staff.mcp.use, with the extra proof below). Credit references stay out: they have no account, they answer through a secure link. Refused with 403 before any tool runs: unknown or inactive users, references, legacy and shelved platform roles, suspended or denied organizations, and roles without the mcp.use cell. A person who never signed in to the web app has no linked identity and is refused: email matching is deliberately not used.

The assistant never has more than the person: a tool is listed and callable only when the person's role holds one of its permission keys (scope own or org honored) AND the AGENT_MCP ceiling allows the tool. Staff edit both live in the permission matrix.

Staff sessions#

A staff MCP session needs the same proof as a staff web sign-in. All of these, on every request, or it is a 403 (fail closed):

  1. The token's org_id claim equals the staff WorkOS organization. AuthKit puts the organization the person selected at consent into the access token (WorkOS: organization access); a token without it, for another organization, or from a Google sign-in never counts.
  2. The database user is an ACTIVE platform operator whose mailbox is a verified staff mailbox (@vendorlogic.com or the explicit allowlist), checked with the same classifyLogin rule as the web sign-in.
  3. WorkOS still reports an active membership of that person in the staff organization right now (live call). If WorkOS cannot be reached the answer is 403.

The role is the database role, narrowed (never raised) by the live WorkOS role. MCP never creates or changes users, so an agent token cannot create or widen staff authority, and a non-staff token (even one that claims the staff organization) is refused and never sees a staff tool. A staff session sees only the read-only staff_* tools.

The token proves who the person is, and for staff which organization they selected. Role, organization and permissions are read from the database on every call, so removing access works at once.

How an agent connects#

  1. POST /v1/mcp with no token answers 401 and WWW-Authenticate: Bearer resource_metadata="https://<host>/.well-known/oauth-protected-resource/v1/mcp".
  2. GET /.well-known/oauth-protected-resource (also with the /v1/mcp suffix) returns RFC 9728 metadata: resource, authorization_servers (WorkOS AuthKit), scopes_supported (openid profile email). It answers 404 when MCP is not configured.
  3. The client follows the authorization server's own metadata, registers (CIMD or dynamic registration), signs the person in with PKCE and requests a token for the resource URL.
  4. Every call sends Authorization: Bearer <token>. The token must be RS256, from the configured issuer, with audience equal to the MCP URL.

Claude: Settings, Connectors, add a custom connector with the URL above. Claude Code: claude mcp add --transport http vendorlogic https://<host>/v1/mcp, then /mcp to sign in.

Endpoint behavior#

RequestResult
POST /v1/mcp validJSON-RPC response (tools/list, tools/call)
POST without or with a bad token401 (a bad token adds error="invalid_token")
POST for a refused person403 with a JSON-RPC error and a message that says why
token issued before the user's revocation cutoff, or organization revoked401, sign in again
GET, DELETE405, Allow: POST (no push stream, no sessions)
cross-site browser POST403 from the Origin guard

Tools#

Each tool lists the permission the person needs (any one of them) and the ceiling key mcp.<tool> an agent role must allow. Scope: a supplier role with applications.view at own sees only applications it sourced; a customer sees its own applications, or the organization's at org scope (a Buyer is own).

ToolKindNeeds permissionNotes
list_applicationsreadapplications.viewfilters status, q, limit (1-50, default 20)
get_applicationreadapplications.viewtax id masked and bank fields omitted unless the caller has applications.decide; owners and officers as a count, whether a personal guaranty is required, how many guaranties are signed, and per owner the title, ownership percent and signed flag (never names, emails or SSN); the credit agreement stage for people with signatures.view
get_application_documentsreaddocuments.viewname, type, review status, size, upload time and scan status (pending, clean, rejected, unavailable); the same visibility as the web, so a customer sees only their own and generated documents; no content, links or storage paths
get_application_referencesreadreferences.viewstage (not_sent, sent, opened, responded, declined, no_response), whether the link expired, reminder count and last reminder, and nextAutoChaseAt (null when no automatic chase is coming); no contact details or links
get_decision_historyreaddecisions.viewno signature image
get_application_messagesreadmessages.viewcustomers get the thread shared with them; suppliers also internal notes; cannot post
get_pipeline_summaryreadreports.viewcounts and totals by status, approval rate
propose_decisionwriteapplications.decideadds a proposal note to the application timeline. It decides nothing and notifies nobody
remind_referencewritereferences.remindstandard reminder only; same rules as the web app (once per 24 hours, not for declined references)
staff_list_tenantsread, staffstaff.tenants.viewfilters q, status, kind
staff_get_tenantread, staffstaff.tenants.viewapproval state, SSO linked flag, counts, KYB status, skipped onboarding steps
staff_list_vendor_requestsread, staffstaff.vendor_requests.vieworganizations asking to become suppliers
staff_platform_healthread, staffstaff.monitoring.viewthe health observations the staff health page shows (service, status, how verified, summary), email queue counts and today's AI spend against the daily cap; runs no checks
staff_search_auditread, staffstaff.audit.viewfilters action, kind, agent, org, targetType, targetId; no metadata payloads

Every application id is resolved inside the caller's reach; a foreign id is "not found". There is deliberately no tool to record a decision, write free text to a person, add a reference, download a document or hand out a link to one, show an owner's name or SSN, impersonate, run monitoring checks, edit the permission matrix or approve a tenant or vendor request.

get_application reads the application's owners and officers, which are personal data. Like the supplier detail page it records a pii.read event (who looked, how many; never the values) before it answers, and the read fails if that event cannot be written.

Audit, limits and errors#

  • Every audit event written while a tool runs is stamped actorKind = agent with the real person as actor, plus the assistant: agentClientId (OAuth client_id), agentName (label derived from it: the host of a client-metadata URL, otherwise empty), agentSessionId (the token sid, one consent grant) and agentTool. This includes events written by shared services, such as the normal reference.reminded. Reads record mcp.read (staff: mcp.staff_read), propose_decision records mcp.decision_proposed in the same transaction, and a refused tool call records mcp.tool_denied with a reason code. Never the data itself.
  • The staff audit log (/platform/audit) shows an Agent badge, the client and the person it acted for, filters by kind and agent, and exports the same columns.
  • Writes are limited to 30 per user per hour, then a rate-limit error with the retry time.
  • A failed tool call is a result with isError: true and a message we wrote: a missing permission ("Your role does not have the ... permission."), a tool the agent limit switched off ("AI agents are not allowed to use this tool right now."), a staff tool outside a staff session ("This tool is not available to your account."), validation problems, "Application not found.", or "The tool failed." for anything unexpected.
  • MCP_AUTHKIT_ISSUER unset means MCP answers 401 "MCP sign-in is not configured."

Applicant names, notes and reference text are typed by third parties and reach the agent through read tools. Read payloads carry a notice to treat them as data. An agent can still repeat what the signed-in person may read into its own conversation.