The Fieldstub MCP server
Fieldstub speaks the Model Context Protocol, so an assistant can turn a description of what happened on site into a structured ticket, and see exactly where the money would land in Procore before anyone approves it.
Endpoint: https://app.fieldstub.com/mcp. Authentication is the same API key as the REST API, sent as a bearer token.
It is listed in the official MCP registry as com.fieldstub/fieldstub, so a client that reads the registry can find it without being told the URL.
Connecting#
Create a key at /keys in the review queue, then point your client at the endpoint with an Authorization header.
In Claude Code that is one line:
claude mcp add --transport http fieldstub https://app.fieldstub.com/mcp \
--header "Authorization: Bearer fs_live_..."
Anywhere that takes a config file:
{
"mcpServers": {
"fieldstub": {
"url": "https://app.fieldstub.com/mcp",
"headers": { "Authorization": "Bearer fs_live_..." }
}
}
}
Streamable HTTP, stateless. No session to establish and nothing to keep alive: one POST, one response. Protocol versions 2025-11-25 and earlier are supported, which is what clients speak today.
Finding it#
The server is published in the official Model Context Protocol registry:
| Field | Value |
|---|---|
| Name | com.fieldstub/fieldstub |
| Transport | streamable-http |
| URL | https://app.fieldstub.com/mcp |
The namespace is verified by domain ownership rather than a username, so com.fieldstub/* can only be published by somebody who controls fieldstub.com. The proof is public at /.well-known/mcp-registry-auth if you want to check rather than take our word for it.
The tools#
| Tool | What it does |
|---|---|
list_projects | Procore projects on your company |
project_capability | Whether a project can take a Change Event, or only a daily log. Read this before approving |
list_tickets | Tickets, filterable by status and project |
get_ticket | One ticket plus routing: where every line will land |
create_ticket | Record extra work. Safe: nothing reaches Procore |
approve_ticket | Writes to Procore. Marked destructive. Takes destination on a project with no Financials |
reject_ticket | Close a ticket without writing |
search_cost_codes | Find a cost code on a project |
create_budget_code | Create a budget code the project lacks |
create_capture_link | A no-login URL for the field |
Two destinations, and the agent has to pick#
Change Events live inside Procore's Project Financials. On a project without it there is no Change Events tool and no change event to write, so approve_ticket fails rather than improvising:
no_change_events: This project has no Change Events tool ...
Approve with destination "daily_log" to file the record to the Procore
daily log instead. That is a diary entry: it carries the description,
hours and who signed, it bills nothing, and it cannot carry the photos.
Call project_capability first on a project you have not seen:
{ "change_events": false, "daily_log": true, "destination": "daily_log" }
destination is an enum of exactly two values and neither is a way around routing. On a project that does have Financials, requireExact is still on and an unconfirmed line is still refused.
What we did about the obvious risk#
An agent with a tool that writes into someone's construction financials is a bad idea handled carelessly. Four things, all of them enforced by the server rather than requested in a prompt.
Approval is marked destructive. approve_ticket carries destructiveHint, so clients that ask before destructive calls will ask. The reads are marked read-only so they do not.
There is no way to accept a blank budget code. The REST API has allow_unrouted for the case where somebody knowingly files a cost to the blank code. That parameter does not exist on the MCP tool, and passing it does nothing. It lives in the web review queue, where a person makes that call.
A merely plausible match is refused too. This is the subtle one. A line with no cost code still resolves to a code of the right cost type: real, plausible, and quite possibly wrong. A reviewer sees that flagged on screen and catches it. Nobody is on screen when an agent calls, so over MCP those lines are refused and must be placed explicitly with routes.
Every response carries a review_url. The moment an agent should stop and hand off, it already has the link to send a person to.
What a good flow looks like#
- Someone describes the extra work, in a message, a voice note, or a thread.
- The assistant calls
create_ticket. Nothing has touched Procore. - It calls
get_ticketand readsrouting. - It shows a person the total and the budget code each line will hit.
- On an explicit yes, it calls
approve_ticket. If a line is not on a confirmed code, the call is refused and it either suppliesroutesor sends the person toreview_url.
Steps 1 to 4 are where the value is. Step 5 is the one to be careful with, and it is fine to decide your assistant never does it.
Why it is a wrapper, not a second product#
Every tool is one call into the same service layer the web review queue and the REST API use. There is one implementation of approving a ticket, so the MCP path cannot quietly develop different rules from the screen a project manager uses.
The one deliberate difference is the direction that makes it stricter, never looser: MCP always demands confirmed budget codes.
Questions
Which MCP protocol version does it support?
2025-11-25 and earlier. Revision 2026-07-28 removes sessions and the initialize handshake and adds required Mcp-Method and Mcp-Name headers; the server is stateless already, so that upgrade is additive once clients start asking for it.
Does it support OAuth?
Not yet. Authentication is a bearer API key from /keys, which works with any client that lets you set a header. OAuth 2.1 with protected resource metadata is what the spec wants for remote servers and is on the list.
Can an agent approve a ticket on its own?
Technically yes, and we would rather it did not. The tool is marked destructive so clients prompt, it refuses any line not on a confirmed budget code, and it cannot accept the blank code at all. Creating and reviewing tickets is the part worth automating.
What stops one customer's agent seeing another customer's tickets?
The API key resolves to exactly one Procore company and every call is scoped to it. A ticket belonging to another company reads as though it does not exist.
Is there a stdio version for local use?
No. The server is remote because the data is, and a local process would still need the same key to reach it.
Put a code on one job and see what comes back.
Fieldstub turns a phone and a QR code into a signed Change Event in Procore. The field needs no login and no seat.
Start free