MCP
MCP
The Constructable MCP server is https://api.constructable.ai/mcp. It OAuth-authenticates a user and runs GraphQL through tools.
MCP is the supported path for assistants. Integrations that call GraphQL directly should use organization API OAuth instead. The MCP server is:
https://api.constructable.ai/mcp
The connection runs as the authorizing user. It cannot widen access beyond that user's organization, project, and record permissions.
Product setup for Claude and ChatGPT is in Connect an outside assistant. This page is the protocol surface.
OAuth
MCP uses OAuth 2.1 with PKCE and dynamic client registration. Discovery:
https://api.constructable.ai/.well-known/oauth-authorization-serverhttps://api.constructable.ai/.well-known/oauth-protected-resource- MCP aliases of both under
.../mcp
The only scope is mcp. An mcp token cannot call /graphql; an api token cannot call /mcp. See Authentication for endpoint URLs and grant types.
After the user confirms the client on Connect MCP client, send the access token as Authorization: Bearer to /mcp.
Permissions
| Role action | Tools |
|---|---|
mcp.read | Required. Without it, the token cannot use MCP. |
mcp.write | Adds write tools. Without it, only read tools are registered. |
Product permissions still apply inside every tool. mcp.write without rfis.write cannot create an RFI.
Tools
list_organizations
Lists organizations the user may use with MCP, and whether the connected role is read-only or write-capable. Call this first. Use the returned organization UUID as organization_id on every other tool. Never pass an organization name or URL.
search_constructable_schema
Runs synchronous JavaScript against the committed GraphQL schema. Use it before query_graphql or mutate_graphql so you do not guess field names.
The code argument is a function expression that receives the introspection spec, for example (spec) => ({ projectFields: fields("Project") }). Do not send a raw expression or a top-level return.
query_graphql
Executes a read-only GraphQL document as that user in the given organization. Mutations are rejected.
Arguments: organization_id, query, summary, optional variables, optional operation_name.
Pass organization and project ids through GraphQL variables. Paginate Relay connections to completion before treating a list as complete.
mutate_graphql
Executes a GraphQL mutation document. Queries are rejected. Only registered when the connection has mcp.write.
Run it only after the user clearly asked for a write. Inspect the mutation field and input type with search_constructable_schema first, then query current records so ids are real.
execute_constructable_script
Runs a synchronous JavaScript function whose first argument is constructable. Use it for multi-step reads (and writes, when allowed) instead of a chain of one-off GraphQL tools.
constructable provides:
queryGraphql— alwaysuuidv7— generate ids for create mutation payloadsmutateGraphql— only on write-capable connections
The code argument must be a function, not a script with top-level return.
GraphQL shape inside MCP
The GraphQL document you pass to MCP tools is the same document you would POST to /graphql:
query ($organizationId: ID!, $projectId: ID!) {
organization(id: $organizationId) {
project(id: $projectId) {
rfis(first: 50) {
nodes {
id
number
subject
status
}
pageInfo {
hasNextPage
endCursor
}
}
}
}
}
Project-scoped mutations stay nested:
mutation ($organizationId: ID!, $projectId: ID!, $input: CreateDraftRfiInput!) {
organization(id: $organizationId) {
project(id: $projectId) {
createDraftRfi(input: $input) {
rfi {
id
status
}
errors
}
}
}
}
When creating records in execute_constructable_script, call uuidv7() for new ids.
Search
For keyword or semantic lookup of drawings, documents, submittals, specs, and other indexed records, use Project.search in GraphQL rather than paging many tables. It is a Relay connection (nodes, pageInfo, first / after).
Logging
MCP requests are logged per user, organization, OAuth application, and session. Write tools are destructive; keep them behind client-side approval in the assistant.