ProWorkflow MCP Server
Model Context Protocol server exposing ProWorkflow resources as tools for AI assistants.
What is MCP?
The Model Context Protocol (MCP) is an open standard for connecting AI assistants to external tools and data. This server lets an assistant such as Claude work in ProWorkflow as the signed-in user, with their permissions.
What it covers
Named tools cover the everyday work: projects, tasks, time, invoices, quotes, requests, files and more. Their arguments are built from the REST API's own documentation, so they always match it. Four generic tools reach every other REST operation (see Any API Operation), so anything the API can do, the assistant can do.
Tool badges
Each tool shows the kind of operation (LIST, GET, CREATE, UPDATE, DELETE) and what it does to data: Read-only, Changes data or Deletes data. Clients get the same information from the tools' annotations, so they can allow reads freely and ask before deleting.
REST API
The MCP server calls the ProWorkflow REST API, with the same permissions. For every operation in detail, see the REST API Documentation.
Using Claude?
Follow Connect Claude to add ProWorkflow as a Claude connector: create an OAuth client, add the server URL in Claude and sign in. No code needed.
https://api.proworkflow.com/api/v4/mcp
{
"jsonrpc": "2.0",
"id": 1,
"method": "tools/call",
"params": {
"name": "list_projects",
"arguments": {
"status": "active",
"pagesize": 10
}
}
}
{
"jsonrpc": "2.0",
"id": 1,
"result": {
"content": [{
"type": "text",
"text": "{\"status\":\"success\",\"data\":[...]}"
}]
}
}
Authentication
Every MCP request needs one of two credentials, the same as the REST API:
- OAuth access token — the request acts as the signed-in staff user, with their permissions. This is how Claude and other AI assistants connect.
- API key — account-level access with no permission checks. For your own scripts and servers.
Connect Claude
A super user creates an OAuth client in ProWorkflow under
Settings → Automation → OAuth Clients,
keeping the prefilled redirect URI
https://claude.ai/api/mcp/auth_callback. Add a custom
connector in Claude with the server URL above and the client's ID
and secret, then each person connects with their own ProWorkflow
login. See
Connect Claude
for every step, including Team and Enterprise plans and Claude Code.
OAuth for other MCP clients
MCP clients find the sign-in server automatically. A request
without valid credentials gets a 401 whose
WWW-Authenticate header points to
/.well-known/oauth-protected-resource, which names
https://identity.proworkflow.com as the authorization
server. Clients can't register themselves, so use the account's
OAuth client: add your client's callback to its redirect URIs and
enter its Client ID and Client Secret in your MCP client. The
OAuth 2.0 guide
covers the flow, scopes and token lifetimes.
API Key
Pass your ProWorkflow API key in the apikey header.
Authentication Scope
The server keeps no session state: every request is authenticated on its own, so a client can refresh its access token between calls. The same permission and division rules as the REST API apply.
https://claude.ai/api/mcp/auth_callback
curl -X POST 'https://api.proworkflow.com/api/v4/mcp' \ -H 'Content-Type: application/json' \ -H 'Authorization: Bearer eyJhbG...' \ -d '{"jsonrpc":"2.0","id":1,"method":"initialize",...}'
curl -X POST 'https://api.proworkflow.com/api/v4/mcp' \ -H 'Content-Type: application/json' \ -H 'apikey: xxxx-xxxx-xxxx-xxxx-PWFxxxx' \ -d '{"jsonrpc":"2.0","id":1,"method":"initialize",...}'
HTTP/1.1 401 Unauthorized WWW-Authenticate: Bearer resource_metadata="https://api.proworkflow.com/.well-known/oauth-protected-resource", scope="openid email"
Conventions
The server sends these instructions to the client when it connects, so the assistant knows the conventions every tool shares:
{
"contacts": "me",
"companyid": "!3",
"duedateto": "+7d",
"fields": "name,duedate,project",
"pagesize": 50,
"pagenumber": 2
}
Any API Operation
The named tools cover the everyday work. For anything else, four generic tools reach every operation in the REST API reference, with the same permissions:
| Tool | Use it to |
|---|---|
find_api_operations | Search every operation by keyword (“invoice phases”, “approve request”). Results name the matching named tool when there is one. |
describe_api_operation | See one operation's path, query and body parameters, with types, required flags and allowed values. |
api_read | Call any GET operation. Read-only. |
api_write | Call any POST, PUT or DELETE operation. The body is an object, or an array where the operation takes one (time allocations). |
Paths take IDs filled in (/projects/12/items);
/api/v4 is optional. Calls are checked against the
API's documentation first: an unknown path or query parameter is
refused with a pointer to the right tool, rather than sent.
File uploads use upload_file.
{ "query": "invoice phases" }
POST /invoices/{invoiceid}/phases: Create Invoice Phases
GET /invoices/{invoiceid}/phases: Get Invoice Phases
...
{ "method": "POST", "path": "/invoices/5/phases" }
{
"method": "POST",
"path": "/invoices/5/phases",
"body": { "name": "Design" }
}
Examples
How an assistant strings the tools together for common requests. Each step is one tool call.
“What's due this week?”
list_projectitemswithcontacts: "me",status: "active"andduedateto: "+7d".
“Log 90 minutes on the homepage task”
get_mefor the user's contact ID.list_projectitemswithq: "homepage"to find the item.create_timewith theitemid,contactid,timespent: 90(minutes) and astarttime.
“Invoice the Acme quote”
list_quoteswithq: "Acme"to find the quote.list_lookupwithkind: "invoice_terms"to choose a payment term.create_invoice_from_quotewith thequoteidandinvoicetermid.
“Add Sam to the Website project and give them the design tasks”
list_contactswithq: "Sam", andlist_projectswithq: "Website".add_project_contactswith the project and Sam's contact ID.list_projectitemswith theprojectidandq: "design".add_contacts_to_projectitemswith the item IDs and Sam's contact ID.
“Attach this brief to the project”
upload_filewithpath: "/projects/12/files", afilename, and the file ascontent(text) orcontent_base64.
{
"itemid": 73675,
"contactid": 12,
"timespent": 90,
"starttime": "2026-10-30T09:00:00Z",
"notes": "Homepage layout review"
}
How It Works
The MCP server uses
JSON-RPC 2.0 over Streamable HTTP. All
communication happens via POST /api/v4/mcp.
Step 1: Initialize
Send an initialize request. The server responds with
its capabilities, protocol version and instructions (see
Conventions).
Step 2: List Tools
Send tools/list to discover all available tools and
their input schemas. Each tool has a name, description, and JSON
Schema for its parameters.
Step 3: Call Tools
Send tools/call with the tool name and arguments. The
server executes the operation against the ProWorkflow REST API and
returns the result.
No sessions
The server is stateless: it issues no mcp-session-id,
and every request is authenticated and handled on its own, so
clients can refresh their access token between calls. Responses
are plain JSON, not an event stream.
{
"jsonrpc": "2.0",
"id": 1,
"method": "initialize",
"params": {
"protocolVersion": "2025-03-26",
"capabilities": {},
"clientInfo": {
"name": "my-client",
"version": "1.0.0"
}
}
}
{
"jsonrpc": "2.0",
"id": 2,
"method": "tools/list"
}
{
"jsonrpc": "2.0",
"id": 3,
"method": "tools/call",
"params": {
"name": "get_company",
"arguments": {
"companyid": 42
}
}
}
Errors
Tool errors are returned inside the
CallToolResult with isError: true. The
error message is in the content array as a text
block.
Common Error Scenarios
| Scenario | Error Message |
|---|---|
| Missing required argument | missing required argument(s): projectid |
| Unknown argument (a typo, or a filter the operation doesn't have) | unknown argument(s): search. Valid arguments: fields, q, status, ... |
| Generic tool path that isn't an API operation | no API operation GET /nowhere. Use find_api_operations to search for one. |
| Invalid credentials | API error (HTTP 401): ... |
| Permission denied | API error (HTTP 403): ... |
| Record not found | API error (HTTP 404): ... |
| Validation error | API error (HTTP 400): ... |
| Rate limited | API error (HTTP 429): ... |
JSON-RPC Errors
Protocol-level errors (invalid JSON-RPC, unknown method) return standard JSON-RPC error responses with negative error codes.
{
"jsonrpc": "2.0",
"id": 3,
"result": {
"content": [{
"type": "text",
"text": "API error (HTTP 403): {\"status\":\"error\",\"data\":[\"Permission denied\"]}"
}],
"isError": true
}
}
{
"jsonrpc": "2.0",
"id": 1,
"error": {
"code": -32601,
"message": "Method not found"
}
}