standards
Agent workflowCookbooks

Agent workflow

Connect Claude Code, read and write records through the MCP tools, then watch a read-only key remove the write tools and refuse a call.


Standards is an MCP server at https://api.standards.new/v1/mcp. Every object your key can read becomes a tool surface, under the same permissions as the REST API. This cookbook walks one session on a contacts object, then narrows the key and shows both refusal shapes on the wire.

What you will do

  • Connect Claude Code with OAuth, or with an API key.
  • Read the instructions the server sends on connect.
  • get_schemasearch_recordscreate_recordupdate_record.
  • Switch to a read-only key: the write tools vanish from tools/list, and a visible tool can still be refused inside a call.

Prerequisites

  • A workspace with a contacts object (any object with a name attribute works; substitute its name).
  • A member account for the OAuth flow, or an API key whose role grants read, create and update on contacts.
  • Claude Code, or any MCP client that speaks Streamable HTTP.

Connect Claude Code

With OAuth, the client needs only the URL. The hosted platform discovers the client through its Client ID Metadata Document; dynamic client registration is disabled. Login opens a browser tab, then a consent screen where you pick the workspace and the scopes.

claude mcp add --transport http standards https://api.standards.new/v1/mcp
claude mcp add --transport http standards \
  https://api.standards.new/v1/mcp \
  --header "Authorization: Bearer stndrds_your_api_key"

Both paths land on the same server. What differs is how the tool catalogue is narrowed: OAuth scopes (mcp:read, mcp:write, mcp:delete, mcp:people:read) for a delegated session, the key's roles for an API key.

Read the instructions

The server sends an instructions string with the handshake. Claude Code shows it under /mcp; it lists the objects this key can read, then the four fixed usage rules quoted on What the client receives.

The index is bounded by object count. Attribute detail belongs to get_schema, not to the handshake.

Read, then write

Ask Claude Code: "Add Ada Lovelace to contacts, then set her role to Analyst." Following the rules above, it makes four calls. Here is what travels for each.

get_schema takes an optional objectNames filter and answers the readable objects with their attributes:

{ "name": "get_schema", "arguments": { "objectNames": ["contacts"] } }

search_records looks for an existing record first. With query it is full-text; without it, a direct read. The compact answer carries [id, label] per record, total and hasMore:

{ "name": "search_records", "arguments": { "objectId": "contacts", "query": "Ada Lovelace", "limit": 5 } }
{
  "records": [],
  "total": 0,
  "hasMore": false,
  "objectLabel": "Contact",
  "objectName": "contacts"
}

No match, so create_record writes one. data is validated against the object's attributes before anything runs, like every tool; an unknown key is rejected:

{ "name": "create_record", "arguments": { "objectId": "contacts", "data": { "name": "Ada Lovelace", "email": "[email protected]" } } }
{
  "record": { "id": "3f0b6d1e-…", "label": "Ada Lovelace", "objectName": "contacts" },
  "recordId": "3f0b6d1e-…",
  "label": "Ada Lovelace",
  "objectLabel": "Contact",
  "message": "Created record: Ada Lovelace"
}

update_record takes the record id, so no object name is needed:

{ "name": "update_record", "arguments": { "recordId": "3f0b6d1e-…", "data": { "role": "Analyst" } } }

Every call is re-authorised per object. A record id resolves to its object first, and the actor's permission on that object decides.

Switch to a read-only key

Now connect with a key whose role grants read on contacts and nothing else. Create it in the app from a read-only role (a key created from the CLI inherits the roles of the key that created it, which is more than you want here).

claude mcp remove standards
claude mcp add --transport http standards \
  https://api.standards.new/v1/mcp \
  --header "Authorization: Bearer stndrds_readonly_key"

tools/list now answers the read tools only. The write tools were not merely disabled: they are not registered for this actor, so the client never sees them.

["bulk_search", "get_record", "get_schema", "global_search", "list_record_drive", "read_document", "search_records"]

Calling a withheld tool by name is a -32602 JSON-RPC error, Tool create_record not found, answered before any tool code runs; the shape is on Tools.

A visible tool, refused inside the call

A key can hold create on one object and not on another. Say the role grants create on tasks and read on contacts. create_record is then in tools/list, because some object grants create, but a call on contacts is refused by the tool itself. The result is a normal tools/call answer with isError set:

{
  "content": [
    {
      "type": "text",
      "text": "{\"error\":\"Access denied: not authorized to create \\\"contacts\\\"\"}"
    }
  ],
  "isError": true
}

The text decodes to the tool's own { error } object: Access denied: not authorized to create "contacts". The message is deterministic, so the agent can read the action and the object and report back instead of retrying.

Two shapes, then: -32602 when the tool is absent from the catalogue, "isError": true when the tool exists but this object is out of reach.

The workspace directory

list_workspace_users is the one tool that does not follow object permissions. It answers to the people:read system permission, the same one GET /users requires. A key holding read on every object still gets no member list without it. Over OAuth, the mcp:people:read scope exposes that single tool, without granting or requiring record read.

Deletes

delete_record and archive_document appear only when a role grants delete. The server's instructions ask the agent to confirm with you before calling either; the delete itself is reversible.

Next steps

  • MCP tools: the full catalogue, what each tool reads or writes, and every refusal.
  • OAuth: scopes, the CIMD rules, consent and token lifetimes.
  • Workspaces and API keys: the roles and permissions behind the narrowing.
  • Status page: the workspace this agent could report incidents into.