Documentation

Connect an AI assistant to Nod

Nod speaks the Model Context Protocol. An assistant can read your projects, versions, comments and decisions, and draft on your behalf. It cannot approve anything, and it cannot download your work.

https://nod.byrewired.com/api/mcp
Transport
Streamable HTTP
Authorization
OAuth 2.1 + PKCE, or an API key
Protocol revisions
2025-03-26 … 2026-07-28
1

The endpoint

One URL, over Streamable HTTP. Most apps only ever need this line.

Authorization is discovered from it: the endpoint answers an unauthenticated call with a challenge naming these two documents, and every conforming client follows them without being told.

https://nod.byrewired.com/.well-known/oauth-protected-resource/api/mcp
https://nod.byrewired.com/.well-known/oauth-authorization-server

Protocol revisions 2025-03-26 through 2026-07-28 are all served from that one URL. A request that omits the version header is read as 2025-03-26, which is what the specification says to do.

2

Six apps, six ways

Two of these take the URL and open a browser to ask you for permission. The other four are configured by editing a file, where there is nowhere for a browser to send you back to, so they use an API key instead. Both carry the same permissions.

Claude CodeApproved in your browser
Run this in any terminal.
claude mcp add --transport http --scope user nod https://nod.byrewired.com/api/mcp
--scope user keeps it in your global config; without it the server belongs to whichever folder you were standing in.
Claude DesktopApproved in your browser
Settings, then Connectors, then Add custom connector.
https://nod.byrewired.com/api/mcp
Paste the URL only. It registers itself and opens the approval page.
ChatGPTApproved in your browser
Settings, then Connectors, then add an MCP server.
https://nod.byrewired.com/api/mcp
Needs developer mode on some plans.
CursorNeeds an API key
~/.cursor/mcp.json, or .cursor/mcp.json in a project.
{
  "mcpServers": {
    "nod": {
      "url": "https://nod.byrewired.com/api/mcp",
      "headers": {
        "Authorization": "Bearer nod_sk_your_key_here"
      }
    }
  }
}
VS CodeNeeds an API key
.vscode/mcp.json in your project.
{
  "servers": {
    "nod": {
      "type": "http",
      "url": "https://nod.byrewired.com/api/mcp",
      "headers": {
        "Authorization": "Bearer nod_sk_your_key_here"
      }
    }
  }
}
ZedNeeds an API key
settings.json, under context_servers.
{
  "context_servers": {
    "nod": {
      "source": "custom",
      "url": "https://nod.byrewired.com/api/mcp",
      "headers": {
        "Authorization": "Bearer nod_sk_your_key_here"
      }
    }
  }
}

Make a key in Settings, then AI connections, and paste it in place of the placeholder. A key acts as whoever made it and works until it is revoked, so keep it out of anything you share.

3

Permissions

Four, and you tick the ones you want when you approve a connection. Read is the only one ticked by default.

nod.read8 tools

Read your projects, assets, comments and decisions

  • nod_docket
  • nod_list_projects
  • nod_project_assets
  • nod_asset_versions
  • nod_asset_comments
  • nod_search
  • nod_contracts
  • nod_retainer_ledger
nod.analytics10 tools

Read throughput, cycle time and gate ageing

  • nod_analytics_waiting_on_me
  • nod_analytics_waiting_on_client
  • nod_analytics_money_outstanding
  • nod_analytics_money_agreed_vs_billed
  • nod_analytics_retainer_burn
  • nod_analytics_approval_turnaround
  • nod_analytics_overdue_windows
  • nod_analytics_link_engagement
  • nod_analytics_activity_digest
  • nod_analytics_gate_ageing
nod.comment2 tools

Leave a comment as you

  • nod_post_comment
  • nod_resolve_comment
nod.export2 tools

Start an export and read its status

  • nod_start_export
  • nod_export_status
nod.write22 tools

Create and edit projects, tasks, scope, invoices, contracts, forms and retainers

  • nod_create_project
  • nod_update_project
  • nod_create_folder
  • nod_rename_folder
  • nod_move_folder
  • nod_upload_asset
  • nod_create_task
  • nod_update_task
  • nod_create_baseline
  • nod_add_baseline_line
  • nod_create_change_request
  • nod_create_invoice
  • nod_add_invoice_line
  • nod_create_contract
  • nod_create_form
  • nod_add_form_field
  • nod_book_consumption
  • nod_price_change_request
  • nod_update_contract_facts
  • nod_create_section
  • nod_open_retainer_period
  • nod_close_retainer_period
nod.send6 tools

Send what it has drafted to your clients

  • nod_send_invoice
  • nod_send_contract
  • nod_send_change_request
  • nod_publish_form
  • nod_assign_form
  • nod_issue_review_link

An assistant that needs more asks again: a tool it cannot reach answers with the exact permission it wants, and the app can send you back to the approval screen for that one thing.

You can always grant less than an app asks for. A connection approved with read only stays read only, whatever the app requested, and nothing about it changes until you approve it again.

4

What it can do

50 tools, grouped by the permission that unlocks them. Each one runs as the person who approved the connection, under exactly the access they have in the browser, so an assistant can never read a project its owner cannot.

nod.read

nod_docket
The approval decisions currently waiting on this account: which asset, which project, which gate, which side owes the next move, and how many days it has waited. Start here for 'what needs deciding'.
nod_list_projects
Projects visible to this account, most recently touched first, with status and due date.
nod_project_assetsproject_id
Assets in one project with their current approval status and gate state. Metadata only — never the file itself.
nod_asset_versionsasset_id
Every version of one asset, oldest first, with the decision recorded against each. This is how a model answers 'why are we on round four'.
nod_asset_commentsasset_id
The comment thread on one asset, oldest first, with the author, the version it was left against, the timecode when there is one, and the text.
nod_searchquery
The product's own search across projects, assets, comments, tasks, people, forms and invoices. Use it when you have a name and need an id.
nod_contracts
Contracts and estimates visible to this account, with status, number, effective date and the parties. The executed text is a resource — nod://contract/{id}.
nod_retainer_ledgerretainer_id
How much of a retainer's allowance has been drawn, by period. Answers 'do we have hours left this month' without anyone opening a spreadsheet.

nod.analytics

nod_analytics_waiting_on_me
How many decisions are sitting with the agency, and how long the oldest has waited. Aggregates only: counts, sums and medians, never the rows behind them.
nod_analytics_waiting_on_client
How many decisions are sitting with clients, and how long the oldest has waited. Aggregates only: counts, sums and medians, never the rows behind them.
nod_analytics_money_outstanding
What has been invoiced and not paid, and how much of it is overdue. Aggregates only: counts, sums and medians, never the rows behind them.
nod_analytics_money_agreed_vs_billed
What the scope baselines committed to, against what has actually been invoiced. Aggregates only: counts, sums and medians, never the rows behind them.
nod_analytics_retainer_burn
How much of each retainer's current allowance has been consumed. Aggregates only: counts, sums and medians, never the rows behind them.
nod_analytics_approval_turnaround
How long a version usually waits before somebody decides on it. Aggregates only: counts, sums and medians, never the rows behind them.
nod_analytics_overdue_windows
Deliveries whose acceptance window has closed without an answer. Aggregates only: counts, sums and medians, never the rows behind them.
nod_analytics_link_engagement
How many review links were issued, how many were opened, and how many are still live. Aggregates only: counts, sums and medians, never the rows behind them.
nod_analytics_activity_digest
Versions, notes and decisions in a window, as counts. Aggregates only: counts, sums and medians, never the rows behind them.
nod_analytics_gate_ageing
Open approval gates, bucketed by how long they have been waiting. Aggregates only: counts, sums and medians, never the rows behind them.

nod.comment

nod_post_commentasset_id, body
Leave a comment on an asset as this account. The only write this server exposes: it cannot approve, reject, upload or delete.
nod_resolve_commentcomment_id
Mark a comment thread settled, or reopen one. Only a ROOT comment can be resolved — a thread is resolved, not an individual reply.

nod.export

nod_start_exportproject_id
Queue an export of a project's APPROVED work. Returns a job id; poll nod_export_status. The package contains only what has been approved, which is the same package the product builds.
nod_export_statusjob_id
The state of one export job, and when it has finished, how large it is and how many files. The download link itself is issued by the product, not here.

nod.write

nod_create_projectname
Create a project in this workspace. Returns its id, which every other project tool takes. A client project is visible to the clients you give access to; an internal one never is.
nod_update_projectproject_id
Change a project's name, description, dates or status. Only the fields you pass are touched. Status is active, on_hold, completed or archived.
nod_create_folderproject_id, name
Create a folder in a project, optionally inside another folder. This is how work is organised for review, so build the shape here rather than approximating it with tasks.
nod_rename_folderfolder_id, name
Rename a folder. Nothing inside it moves.
nod_move_folderfolder_id
Move a folder under a different parent, or to the top of the project by omitting the parent. Everything inside it moves with it.
nod_upload_asset
Upload a file into a project as a new asset, or as a new version of one that exists. Give it either a source_url the server can fetch, or content_base64 for something small. The file is encrypted before it is stored, exactly as the app encrypts it.
nod_create_taskproject_id, name
Create a task on a project, optionally assigned and dated. Calls the product's own create_task, so sections, subtasks and positions behave exactly as they do in the app.
nod_update_tasktask_id
Rename a task, move its dates, change its assignee, or mark it complete. Only what you pass is touched. Completing calls the product's own complete_task so followers and activity behave normally.
nod_create_baselineproject_id
Start a new scope baseline on a project, as a draft. Version numbers follow the project's existing baselines. Add lines with nod_add_baseline_line; a draft binds nobody until it is activated in the app.
nod_add_baseline_linebaseline_id, title
Add one deliverable line to a draft baseline. Line numbers are assigned in order. unit_amount is in the currency's minor units, so 1500 is 15.00.
nod_create_change_requestproject_id, title, body_md
Raise a change request against a project, as a draft. Send it to the client with nod_send_change_request. Deciding it stays with people and is not something this server can do.
nod_create_invoicecurrency
Start an invoice or estimate as a draft. Add lines with nod_add_invoice_line, then send it with nod_send_invoice. Marking one paid is not something this server can do.
nod_add_invoice_lineinvoice_id, description, unit_price
Add one line to a draft invoice and recompute its totals. unit_price is in minor units, so 1500 is 15.00. tax_rate_bps is basis points, so 2000 is 20 percent.
nod_create_contracttitle, body_md
Start a contract or estimate as a draft, with a markdown body. Send it for signature with nod_send_contract. Signing and countersigning are not something this server can do.
nod_create_formtitle
Create a form as a draft. Add questions with nod_add_form_field, publish it with nod_publish_form, then assign it to people with nod_assign_form.
nod_add_form_fieldform_id, type, label
Add one question to a draft form. Choices for single_choice and multi_choice go in config, as {"options": ["a", "b"]}. A section field is a heading rather than a question.
nod_book_consumptionretainer_id, quantity
Book consumption against a retainer's open period. Calls the product's own book_consumption, so the burndown, the allowance and any overage behave exactly as they do in the app.
nod_price_change_requestchange_request_id, amount, currency_code
Say what a change request costs and what it does to the date. A change request cannot be sent until it has a price, so do this before nod_send_change_request.
nod_update_contract_factscontract_id
Set the parties, the money and the terms on a DRAFT contract. These are the values the document's {{tokens}} read from, so filling them is what turns a template into a contract. Only what you pass is touched.
nod_create_sectionproject_id, name
Create a stage — shoot, edit, delivery — that tasks are grouped under. Pass its id to nod_create_task to put a task straight into it.
nod_open_retainer_periodretainer_id
Start the next cycle on a retainer. Work cannot be booked against a retainer with no open period, and only one can be open at a time.
nod_close_retainer_periodperiod_id
End the current cycle. Nothing more can be booked against it and whatever is left follows the retainer's rollover rule. This cannot be undone.

nod.send

nod_send_invoiceinvoice_id
Send a draft invoice to its client. This leaves the workspace and cannot be unsent. Marking it paid afterwards stays with people.
nod_send_contractcontract_id
Send a draft contract to its counterparty for signature. This leaves the workspace and cannot be unsent. Signing it is not something this server can do.
nod_send_change_requestchange_request_id
Send a drafted change request to the client for a decision. The decision itself is theirs and is not something this server can record.
nod_publish_formform_id
Publish a draft form so it can be assigned and answered. Its questions are fixed once published.
nod_assign_formform_id, user_ids
Assign a published form to one or more people, who are notified. Takes their user ids.
nod_issue_review_linkgate_id, asset_id
Issue a review link for a gate and asset, so somebody outside the workspace can look and decide. The link carries no password, so treat the returned address as a credential.

The names beside a tool are the arguments it insists on. Everything else it takes is optional and has a sensible default.

5

What it can read

Context an assistant can attach once instead of fetching every turn. Useful when a conversation is about one project for an hour.

nod://project/{project_id}application/json
A project: brief, gates, dates, state
nod://project/{project_id}/docketapplication/json
What is waiting on whom in one project, right now
nod://asset/{asset_id}/versionsapplication/json
Every version of an asset with the decision on each
nod://contract/{contract_id}text/plain
The executed text of a contract, as text
nod://retainer/{retainer_id}/ledgerapplication/json
Drawdown against allowance, by period
6

Ready-made prompts

Three jobs an account manager does every week, with the context already gathered. Each one ends in a draft for a person to send, never in a decision.

chase_the_gate(project_id)
Who is late on this project, on what, for how long, and the note to send them. Ends in a draft; a person sends it.
client_round_up(project_id, since, locale)
What moved on this project since a date, written for the client rather than for the agency.
scope_check(project_id, request)
Whether a request sits inside the agreed baseline or is a change request, and the reasoning either way.
7

What it can never do

Not settings, and not a plan. There is no tool for any of these at any permission level, which is why the permission list has only four entries.

  • Approve, reject, sign or accept. There is no tool for it at any permission level, and there will not be: an approval is a recorded decision by a named person, and in this market it is increasingly the document that triggers payment. An assistant can read the whole thread and draft the reply. A person presses the button.
  • Return your work. No tool, resource or prompt hands back a frame, a render or a file. Delivery stays behind the watermarking and expiring links the product already uses.
  • See past the person who approved it. Every query runs as that account under the same rules the browser enforces, so an assistant connected by a client sees a client's view, not the agency's.
  • Send anything to anyone. It cannot issue a review link, invite a person, email a client or change who can see a project. Reaching outside the workspace stays a decision somebody makes in the product.
8

Limits, the record, and cutting it off

120 calls a minute per connection, counted centrally rather than per server, so the limit is the limit. Past it, calls come back as 429 and the assistant waits.

Every call is recorded with the tool it used, the account it acted as, whether it succeeded and how long it took. Workspace admins can read their own workspace's log; support staff cannot, because tool arguments carry project and asset names.

Disconnecting is immediate. Revoking a connection invalidates every token issued under it in the same statement, so there is no hour-long window where an assistant keeps working.

Access tokens are short lived and refreshed in the background. A refresh token is single use: presenting one twice means two parties hold it, so the whole connection is revoked rather than the second attempt refused.

9

When it does not connect

Four failures cover almost all of it, and each one names itself in the response.

401 invalid_token
The token is missing, expired, or was minted for a different server. Check the URL is exactly the one above, with no trailing slash: tokens are audience bound, and a client that asked for a slightly different address gets a token this server will not accept.
invalid_target
The app asked for a resource that is not this one. Same cause, one step earlier. Remove the connection in the app and add it again with the URL copied from this page.
nod_sk_your_key_here
The placeholder was never replaced. Four of the six apps read a file, and a file needs a real key in it. Make one in Settings, then AI connections.
429
Too many calls in a minute. Nothing is wrong; the assistant is looping. It resolves itself within the minute.

Anything else, open Settings, then AI connections. Every call the workspace has made is there with its outcome, which is usually enough to see what the app is actually asking for.