MCP server
Connect AI clients to Noru over MCP with OAuth or API keys, and understand which tools each scope and role unlocks.
What it is
The MCP server lets an AI client such as Cursor, Claude, or ChatGPT read your compliance program and, when you allow it, change it: list controls, link evidence, draft policies, create risks, push a data map. It runs inside the same API as the REST routes, so a tool call goes through the same permission checks and lands in the same activity log as a click in the app.
Endpoint and transport
| Property | Value |
|---|---|
| URL | POST https://api.noru.tech/v1/mcp |
| Transport | Streamable HTTP, stateless |
GET or DELETE on the endpoint | 405 (there is no session to resume or end) |
| Authentication | Authorization: Bearer with an OAuth access token or an API key |
| Protected-resource metadata | https://api.noru.tech/.well-known/oauth-protected-resource/v1/mcp |
| Authorization server metadata | https://app.noru.tech/.well-known/oauth-authorization-server |
OAuth resource value | https://api.noru.tech/v1/mcp |
Authentication
OAuth (interactive clients)
Clients that can open a browser sign in with OAuth. You never handle a secret, and the token is tied to you, your role, and one organization.
https://api.noru.tech/v1/mcp as a remote MCP server in the client.client_id).| Detail | Value |
|---|---|
| PKCE | Required, S256 only |
| Grant types | authorization_code, refresh_token |
| Client authentication at the token endpoint | none, client_secret_basic, client_secret_post |
| Authorization code lifetime | 60 seconds |
| Pending authorization lifetime | 10 minutes |
| Access token lifetime | 1 hour |
| Refresh token lifetime | 30 days, rotated on every use |
Endpoints on the issuer (app.noru.tech) | /api/oauth/mcp/register, /api/oauth/mcp/authorize, /api/oauth/mcp/token, /api/oauth/mcp/revoke |
Aliases on api.noru.tech | /register, /token, /revoke, and /authorize (redirects to the app) |
The consent screen refuses with "You must belong to a Noru organization to authorize MCP access." if your account has no membership yet, and it rejects an authorization that was started by a different signed-in user.
API keys (headless)
Clients that cannot open a browser, stdio bridges, CI, and scripts, send an
API key in the same Authorization: Bearer header.
The key carries its own scopes and acts as the admin who created it. The
MCP client setup guides show a key-based config
for every supported client.
Scopes and roles
A scope unlocks a group of tools. The scope list is shared with the REST API and documented on the API keys page. OAuth tokens are capped by the approving user's role:
| Role | Scopes an OAuth token may hold |
|---|---|
| Admin | All read and write scopes |
| Editor | All read and write scopes |
| Viewer | Read scopes only |
The cap applies on the consent screen, on token issue, on every token
validation, and on refresh, so a user demoted to viewer loses write tools on the
next request without re-authorizing. A remembered approval is re-narrowed to the
current role each time it is reused. The protocol scope offline_access
(refresh tokens) is not a permission and is available to every role.
If a client calls a tool outside its scopes it receives a 403 with an
insufficient_scope challenge naming the missing scope, and a well-behaved
client will send you back to the consent screen to add it.
What the server exposes
Tools are filtered by scope when the client lists them, so a read-only token
never sees write tools. Write tools re-check the scope when they run. The
server's instructions ask the model to call getMcpCapabilities first; it
reports the active organization, the granted scopes, and the exact tools
visible on that connection.
Organization, users, and frameworks
| Scope | Tools |
|---|---|
read:organization | findOrganization, getMcpCapabilities |
read:users | getUser |
read:frameworks | getAllFrameworks, getOrganizationFrameworks |
write:compliance | createCompliancePlan, suggestComplianceTasks |
Controls and evidence
| Scope | Tools |
|---|---|
read:controls | getOrganizationControls, getPendingControls, getControlStatus, getControlContext |
write:controls | updateControl, setControlStatus, setControlOwner |
read:evidence | getOrganizationEvidence, getEvidenceItems, getEvidenceForControl |
write:evidence | createEvidence, updateEvidence, deleteEvidence, linkEvidenceToControl, unlinkEvidenceFromControl |
getControlContext returns a control's status, owner, linked evidence,
policies, risks, review logs, framework mappings, and coverage in one call;
prefer it over chaining list tools. Control ids are lowercase (sm-01); the
uppercase display id is accepted too.
Policies
| Scope | Tools |
|---|---|
read:policies | getOrganizationPolicies, getPolicyDetails, getPolicyLogs, getPolicyControlMappings |
write:policies | createPolicy, generatePolicies, linkPolicyToControl, setPolicyApprover, setPolicyStatus, setPolicyVersion, updatePolicy |
Risks and security findings
| Scope | Tools |
|---|---|
read:risks | getOrganizationRisks, getRiskDetails, getRiskTreatments, getRiskControlMappings, getRiskLogs, getHighRiskItems, getRiskStatus, getSecurityFindings |
write:risks | createRisk, updateRisk, deleteRisk, setRiskOwner, setRiskStatus, linkRiskToControl, unlinkRiskFromControl, createSecurityFinding, updateSecurityFinding, deleteSecurityFinding, linkSecurityFindingToRisk, unlinkSecurityFindingFromRisk, bulkLinkSecurityFindingsToRisk, createRiskTreatment, deleteRiskTreatment, setRiskTreatmentStatus, updateRiskTreatment |
Vendors and assets
| Scope | Tools |
|---|---|
read:vendors | getOrganizationVendors, getVendorDetails, getMergedVendors, getVendorContacts, getVendorEvidence, getVendorLogs, getVendorPersonnel |
write:vendors | createVendor, updateVendor, deleteVendor, mergeVendors, unmergeVendor, addVendorContact, removeVendorContact, removeVendorEvidence, setVendorOwner, setVendorStatus, updateVendorContact |
read:assets | getOrganizationAssets |
write:assets | createAsset, updateAsset, deleteAsset |
Personnel and training
| Scope | Tools |
|---|---|
read:personnel | getOrganizationPersonnel, listTrainingPlans, getTrainingPlanDetails, listTrainingCampaigns, getTrainingCampaignDetails, listMissingPolicyAcknowledgements, listTrainingPolicyOptions |
write:personnel | createPersonnel, updatePersonnel, deletePersonnel, recordManualTrainingCompletion, recordManualPolicyAcknowledgement, assignTraining, requestPolicyAcknowledgement, createTrainingPlan, updateTrainingPlan, deleteTrainingPlan, activateTrainingPlan, syncTrainingCampaignAssignmentStatuses, sendTrainingCampaignReminders, archiveTrainingCampaign, bulkArchiveTrainingCampaigns, deleteTrainingCampaign |
Privacy data map and AI inventory
These tools additionally require the privacy customer segment; on a compliance-only organization they are not listed even with the scope.
| Scope | Tools |
|---|---|
read:datamaps | getAiRegister, getPrivacyAssessment, getPrivacyDataMap, getPrivacyDataSource, getPrivacyOverview, getPrivacyReviewItem, getPrivacySettings, getPrivacyTaxonomy, getProcessingActivity, getRegulationCoverage, getRopa, listAssessableRecords, listPrivacyAssessments, listPrivacyDataSources, listPrivacyDatasets, listPrivacyReviewItems, listPrivacySecurityMeasures, listPrivacySystems, listProcessingActivities |
write:datamaps | archivePrivacySecurityMeasure, archiveProcessingActivity, createPrivacyAssessment, createPrivacySecurityMeasure, createProcessingActivity, deletePrivacyAssessment, deletePrivacyDataSource, ingestDatamap, linkPrivacyAssessmentRisk, reviewActivityAiSuggestion, setProcessingActivityMeasures, setProcessingActivityRetentionRules, setProcessingActivityTransfers, unlinkPrivacyAssessmentRisk, updateCoveredRegulations, updatePrivacyAssessment, updatePrivacyDataSource, updatePrivacySecurityMeasure, updatePrivacySettings, updatePrivacySystem, updateProcessingActivity |
write:ai-inventory | ingestAiInventory, reviewAiClassification, reviewAiFinding |
Internal audits
| Scope | Tools |
|---|---|
read:internal-audits | getInternalAudit, getInternalAuditReport, listInternalAudits |
write:internal-audits | addInternalAuditFinding, completeInternalAudit, createInternalAudit, updateInternalAuditFindingStatus, updateInternalAuditReport |
Resources and prompts
| Kind | Name | What it gives the model |
|---|---|---|
| Resource | noru://frameworks/compliance-overview | Framework Compliance Overview: enabled frameworks and their coverage |
| Resource | noru://policies/review-queue | Policies Requiring Attention: policies in draft or review |
| Prompt | assessFrameworkGaps (argument frameworkName) | A structured gap assessment for one framework |
| Prompt | policyReviewSummary (argument policyId) | A review summary for one policy |
Tool lists may be cached by the client for 60 seconds, prompt and resource lists for 5 minutes, and server discovery for an hour; all caches are private to the credential.
Confirmations for destructive tools
Tools that delete, archive, merge, or remove records, plus generatePolicies
and the ingest* tools, ask you to confirm before running. The first call returns a form; the client shows it,
and retries with your answer. The confirmation is bound to your credential and
the exact call, and expires after 10 minutes. Declining returns the error code
CANCELLED. Only clients that declare the elicitation capability are asked;
other clients run the tool directly. Noru does not otherwise confirm writes:
updateRisk or setControlStatus apply immediately.
A write scope on an MCP token lets the model change data exactly as you could in the app. Read what the client is about to do before you approve it, and give headless keys only the write scopes their job needs.
Errors
Tool failures are returned as results with isError: true and a JSON body
holding an error object with code and message; they never surface as
JSON-RPC protocol errors, so the model can read them and correct course.
| Code | When |
|---|---|
NOT_FOUND | No record with that id in this organization |
FORBIDDEN | The credential lacks the scope; the body also names the required scope |
INVALID_REQUEST | Arguments failed validation |
DUPLICATE_EXTERNAL_ID, DUPLICATE_EMAIL | A unique field is already taken |
ASSIGNEE_MUST_BE_USER | The owner or assignee must be a user, not a personnel record |
ALREADY_LINKED | The link you asked for already exists |
CREATE_FAILED, UPDATE_FAILED, DELETE_FAILED, LINK_FAILED, UNLINK_FAILED, BULK_LINK_FAILED | The service rejected the write; the message explains why |
CANCELLED | You declined a destructive-tool confirmation |
INTERNAL | An exception the tool did not map |
HTTP-level responses follow the REST error envelope:
401 with a WWW-Authenticate challenge that lists the scopes, 403
insufficient_scope, 429 RATE_LIMITED under the same
500 requests per 10 minutes budget,
and 402 PAYMENT_REQUIRED for evidence tools when billing is not active.
Activity and revocation
Every tool call is recorded in the organization's activity log with source
mcp and the calling user or key as actor, so an AI-made change is as
traceable as a manual one.
| To stop | Do this |
|---|---|
| One client you authorized with OAuth | Remove the Noru server from that client, or have the client POST its token to the revocation endpoint (https://app.noru.tech/api/oauth/mcp/revoke or https://api.noru.tech/revoke). Refresh tokens stop working immediately; an access token already issued lasts at most another hour. |
| A headless client using an API key | Revoke the key under SettingsDeveloper. |
| Future re-use of a remembered approval | Revoke as above; the next connection shows the consent screen again if the client re-registers. |
There is no list of OAuth tokens in Settings today. Revoking an API key never affects OAuth tokens, and revoking an OAuth token never affects keys. If you cannot reach the client that holds a token, contact support to invalidate it.
What the MCP server does not do
- It cannot upload files. Tool arguments are JSON; use
POST /v1/evidence/uploadon the REST API and thenlinkEvidenceToControl. - It cannot create or delete controls; those come from your enabled frameworks.
- It never crosses organizations. One token, one organization; switch by re-authorizing and picking another in the consent screen.
- It does not implement roots, sampling, server logging notifications, tasks, or the legacy HTTP+SSE transport. A client that sends
GETto open an event stream gets405. - A tool never bypasses your role. A viewer's token cannot write, however the client is configured.
- It does not restrict the browser origin a client connects from; the API host holds no cookies or ambient credentials, so every call must carry its own bearer token.
Related
Last updated on