MCP

23 read tools, 41 write tools.

Telos exposes curated tools over the Model Context Protocol so external agents can ground decisions in your product state and act on it. Every tool is gated by the calling key's capabilities; every write is idempotent and attributed to the key's user, through the same service layer as REST.

Connect

Endpoint & auth

POST /api/mcp speaks the Streamable HTTP MCP transport. Auth is the same bearer token you use for REST: Authorization: Bearer telos_live_… (or an OAuth app token, telos_oat_…, limited to its granted scopes). Rate limits apply as on REST, and every tool below is gated by the calling key's capabilities — a read-only key sees writes returned as permission errors.

endpoint
https://www.telos-app.com/api/mcp
Quickstart

Connect a client

Three steps to wire Telos into an MCP client. The same endpoint and token work for any client that speaks the Model Context Protocol — pick yours in step 2.

1. Get an API key

Issue a key from inside the app at Configurations → API Keys. Keys start with telos_live_and carry the role that decides which tools and fields you see — copy it now, it's shown only once. The endpoint is the same origin you use Telos on, with /api/mcp appended — there's no per-account host. For this workspace that's https://www.telos-app.com/api/mcp, which is what the snippets below use.

2. Register the server with your client

Select your client. Most speak Streamable HTTP natively, so you give them the endpoint and token directly — only Claude Desktop's config launches local commands, so it bridges through mcp-remote.

Register the endpoint with one CLI command — run it from any project, or add --scope user to make it available everywhere.

terminal
claude mcp add --transport http telos \
  https://www.telos-app.com/api/mcp \
  --header "Authorization: Bearer telos_live_…"

3. Verify the connection

In a client, the Telos tools should now appear in the tool list. To check the endpoint and token directly, send the protocol handshake with curl — a 200 with a result confirms both connectivity and auth; a 401 means the key is missing or invalid:

verify
curl -sN https://www.telos-app.com/api/mcp \
  -H 'Authorization: Bearer telos_live_…' \
  -H 'Content-Type: application/json' \
  -H 'Accept: application/json, text/event-stream' \
  -d '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2025-06-18","capabilities":{},"clientInfo":{"name":"curl","version":"0"}}}'

Once connected, ask the agent to call whoami to confirm which user and role the key resolves to. The full tool list is below.

Reference

Read tools

Auto-rendered from the source registry. Every input parameter is validated against a Zod schema on the server.

Identity & directory · 3

whoami

Get the identity of the authenticated user.

Input
No parameters.

list_teams

List teams in the organisation.

Input
No parameters.

list_users

List users in the organisation.

Input
No parameters.
Vision · 3

list_visions

List visions in the organisation, most recent first.

Input
No parameters.

get_vision

Get a specific vision by id.

Input
  • id: uuidVision UUID

list_vision_versions

List the version history for a vision.

Input
  • visionId: uuidVision UUID
Objectives · 2

list_objectives

List strategic objectives. Optionally scope to a vision.

Input
  • visionId?: uuidVision UUID filter

get_objective

Get a specific strategic objective by id.

Input
  • id: uuidObjective UUID
Metrics · 2

list_metrics

List metrics tracked by the organisation.

Input
No parameters.

get_metric

Get a specific metric by id.

Input
  • id: uuidMetric UUID
Strategy · 1

list_strategies

List strategies in the organisation. Defaults to active.

Input
  • status?: "draft" | "active" | "archived"Strategy status filter
Opportunities · 3

list_opportunities

List opportunities. Optionally filter by lifecycle state (active/completed/rejected) or team.

Input
  • state?: "backlog" | "in_progress" | "completed" | "cancelled"Opportunity lifecycle state filter
  • teamId?: uuidTeam UUID filter

get_opportunity

Get a specific opportunity by id.

Input
  • id: uuidOpportunity UUID

get_opportunity_context

Get everything needed to act on an opportunity in one call: the opportunity and PRD, linked objectives, the customer(s), comments, linked insights, the active workflow step, and any linked PRs. Sections you lack the read capability for (e.g. customers, insights) come back null. Pass the opportunity ref (e.g. OPP-23).

Input
  • ref: stringOpportunity ref, e.g. OPP-23
Tasks · 3

list_tasks

List tasks. Optionally filter by opportunity, status, priority, or team.

Input
  • opportunityId?: uuidOpportunity UUID filter
  • status?: "backlog" | "todo" | "in_progress" | "completed" | "cancelled" | "duplicate"Task status filter
  • priority?: "low" | "medium" | "high" | "urgent"Task priority filter
  • teamId?: uuidTeam UUID filter

get_task

Get a specific task by id.

Input
  • id: uuidTask UUID

get_task_context

Get everything needed to act on a task in one call: the task spec, its parent opportunity and PRD, the customer(s), comments, linked insights, the active workflow step, and any linked PRs. Sections you lack the read capability for (e.g. the parent opportunity, customers, insights) come back null. Pass the task ref (e.g. TF-24).

Input
  • ref: stringTask ref, e.g. TF-24
Intake · 2

list_intake

List intake items. Optionally filter by status.

Input
  • status?: "new" | "needs_shaping" | "accepted" | "rejected"Intake status filter

get_intake_stats

Get intake queue counts grouped by status and category.

Input
No parameters.
Customers · 2

list_customers

List customers. Optionally filter by status.

Input
  • status?: "prospect" | "onboarding" | "pilot" | "live" | "churned" | "lost"Customer status filter

get_customer

Get a specific customer by id.

Input
  • id: uuidCustomer UUID
Reference

Write tools

Each write wraps the same scoped service the REST API uses, so every mutation is typed, validated, audited, and attributed to the key's user — never a system actor. Each tool requires a specific capability; supply an idempotencyKey on any write you might retry so a repeat returns the first result instead of writing again.

post_update

Post a comment or update on a task or opportunity, authored by the key's user. Kind 'update' on an opportunity is a status broadcast: it lands in the updates stream and flows into every linked customer's feed. Kind 'user' (or any task post) is a plain comment on the entity's thread.

Requires
comment:create
Input
  • entityType: "task" | "opportunity"
  • entityId: uuid
  • body: string
  • kind?: "user" | "update"
  • idempotencyKey?: stringStable key for safe retries — a repeat returns the first result instead of writing again.

create_task

Create a task, attached to an opportunity (opportunityId) or standalone (bug fixes and maintenance are first-class). Optionally seed subtasks, owner, team, category, workflow, and tags in the same call.

Requires
task:create
Input
  • title: string
  • description?: string
  • content?: string
  • status?: "backlog" | "todo" | "in_progress" | "completed" | "cancelled" | "duplicate"
  • priority?: "low" | "medium" | "high" | "urgent"
  • opportunityId?: uuid
  • teamId?: uuid | null
  • ownerId?: uuid | null
  • categoryId?: uuid | null
  • parentTaskId?: uuid
  • subtaskPosition?: integer
  • subtaskMode?: "standalone" | "sequential"
  • subtasks?: object[]
  • workflowId?: uuid
  • onboardingStepInstanceId?: uuid
  • estimateMinutes?: integer
  • dueDate?: string
  • tagIds?: string[]
  • idempotencyKey?: stringStable key for safe retries — a repeat returns the first result instead of writing again.

update_task

Update a task's fields (title, description, content, priority, opportunity, team, category, subtask mode, estimate, due date). For a bare status move prefer set_task_status, which also auto-assigns an owner to ownerless work.

Requires
task:update
Input
  • title?: string
  • description?: string
  • content?: string | null
  • status?: "backlog" | "todo" | "in_progress" | "completed" | "cancelled" | "duplicate"
  • priority?: "low" | "medium" | "high" | "urgent"
  • opportunityId?: uuid | null
  • teamId?: uuid | null
  • workflowId?: uuid
  • categoryId?: uuid | null
  • subtaskMode?: "standalone" | "sequential"
  • estimateMinutes?: integer | null
  • dueDate?: string | null
  • id: uuidTask UUID
  • idempotencyKey?: stringStable key for safe retries — a repeat returns the first result instead of writing again.

set_task_status

Set a task's status. Moving an ownerless task to todo/in_progress assigns the key's user as its owner — work in flight is never ownerless.

Requires
task:update
Input
  • id: uuid
  • status: "backlog" | "todo" | "in_progress" | "completed" | "cancelled" | "duplicate"
  • idempotencyKey?: stringStable key for safe retries — a repeat returns the first result instead of writing again.

write_handoff

Move an opportunity or customer-onboarding workflow to a step and post a handoff message for it. Jumps to the target step instance (optionally completing the rest) and records the handoff note on the entity's thread. For a subtask sequence inside a task, use write_task_handoff instead.

Requires
workflow:update
Input
  • targetStepInstanceId: uuid
  • markComplete?: boolean
  • message?: string
  • mentions?: object
  • mentionLinkUrl?: string
  • idempotencyKey?: stringStable key for safe retries — a repeat returns the first result instead of writing again.

write_task_handoff

Post the handoff note for a completed subtask in a sequential task, briefing the next owner. Complete the subtask first (set_task_status), then write the note; it lands on the parent task's thread and clears the pending-note reminder.

Requires
task:update
Input
  • fromTaskId: uuid
  • note: string
  • idempotencyKey?: stringStable key for safe retries — a repeat returns the first result instead of writing again.

create_opportunity

Create a work opportunity. The description seeds the PRD (or a PRD template does, when omitted). Optionally set lead, team, workflow, category, initial objective links, and tags. Financial fields are ACL-gated.

Requires
opportunity:create
Input
  • title: string
  • description?: string
  • estimatedCost?: number
  • leadId?: uuid
  • teamId?: uuid | null
  • workflowId?: uuid
  • initialState?: "backlog" | "in_progress"
  • categoryId?: uuid | null
  • objectiveLinks?: object[]
  • tagIds?: string[]
  • dueDate?: string
  • prdTemplateId?: uuid | null
  • idempotencyKey?: stringStable key for safe retries — a repeat returns the first result instead of writing again.

update_opportunity

Update an opportunity's title, lead, team, or PRD body. Lifecycle moves go through write_handoff (workflow steps) or reject_opportunity, not here.

Requires
opportunity:update
Input
  • title?: string
  • estimatedCost?: number
  • leadId?: uuid
  • teamId?: uuid | null
  • prd?: string | null
  • id: uuidOpportunity UUID
  • idempotencyKey?: stringStable key for safe retries — a repeat returns the first result instead of writing again.

delete_opportunity

Permanently delete an opportunity and its dependency edges. Prefer reject_opportunity to record a won't-build decision; delete is for mistakes, not outcomes.

Requires
opportunity:delete
Input
  • id: uuid
  • idempotencyKey?: stringStable key for safe retries — a repeat returns the first result instead of writing again.

link_objective

Tie an opportunity to an objective (membership only: 'this bet claims to matter for that number'). Impact evidence lives on insights, not the link.

Requires
opportunity:create
Input
  • opportunityId: uuid
  • objectiveId: uuid
  • idempotencyKey?: stringStable key for safe retries — a repeat returns the first result instead of writing again.

update_objective_link

Repoint an opportunity's objective link at a different objective.

Requires
opportunity:update
Input
  • opportunityId: uuid
  • objectiveId: uuid
  • patch: object
  • idempotencyKey?: stringStable key for safe retries — a repeat returns the first result instead of writing again.

unlink_objective

Remove the tie between an opportunity and an objective.

Requires
opportunity:delete
Input
  • opportunityId: uuid
  • objectiveId: uuid
  • idempotencyKey?: stringStable key for safe retries — a repeat returns the first result instead of writing again.

reject_opportunity

Reject an opportunity: skips remaining workflow steps, lands it on the Cancelled terminal step (or flips a workflow-less one to cancelled), and records the categorized won't-build decision on the thread.

Requires
opportunity:create
Input
  • opportunityId: uuid
  • category: "no_demand" | "not_aligned" | "not_viable" | "not_now" | "duplicate" | "other"
  • reason: string
  • idempotencyKey?: stringStable key for safe retries — a repeat returns the first result instead of writing again.

create_objective

Create a strategic objective under a vision. The objective IS the commitment to a number: it requires a DRI and a metric block (metricId, operator, target).

Requires
objective:create
Input
  • title: string
  • description?: string
  • deadline?: string
  • visionId: uuid
  • driId: uuid
  • metric: object
  • idempotencyKey?: stringStable key for safe retries — a repeat returns the first result instead of writing again.

update_objective

Update an objective's title, description, deadline, DRI, or status.

Requires
objective:update
Input
  • title?: string
  • description?: string
  • deadline?: string | null
  • driId?: uuid
  • status?: "active" | "achieved" | "abandoned"
  • id: uuidObjective UUID
  • idempotencyKey?: stringStable key for safe retries — a repeat returns the first result instead of writing again.

delete_objective

Permanently delete an objective.

Requires
objective:delete
Input
  • id: uuid
  • idempotencyKey?: stringStable key for safe retries — a repeat returns the first result instead of writing again.

set_objective_metric

Replace which metric an objective points at, and/or change its target shape (operator, target, start value). An objective has exactly one metric.

Requires
objective:create
Input
  • objectiveId: uuid
  • metricId: uuid
  • operator: "gte" | "lte" | "eq"
  • target: number
  • startValue?: number | null
  • idempotencyKey?: stringStable key for safe retries — a repeat returns the first result instead of writing again.

create_metric

Create a metric (name, unit, optional data source and steward).

Requires
metric:create
Input
  • name: string
  • description?: string
  • currentValue?: number
  • unit: "percentage" | "count" | "currency" | "ratio" | "duration" | "custom"
  • dataSourceType?: "manual" | "integration" | "system"
  • dataSourceConfig?: object | null
  • stewardId?: uuid
  • idempotencyKey?: stringStable key for safe retries — a repeat returns the first result instead of writing again.

update_metric

Update a metric's name, description, unit, data source, or steward.

Requires
metric:update
Input
  • name?: string
  • description?: string
  • currentValue?: number
  • unit?: "percentage" | "count" | "currency" | "ratio" | "duration" | "custom"
  • dataSourceType?: "manual" | "integration" | "system"
  • dataSourceConfig?: object | null
  • stewardId?: uuid | null
  • id: uuidMetric UUID
  • idempotencyKey?: stringStable key for safe retries — a repeat returns the first result instead of writing again.

delete_metric

Permanently delete a metric.

Requires
metric:delete
Input
  • id: uuid
  • idempotencyKey?: stringStable key for safe retries — a repeat returns the first result instead of writing again.

record_metric_value

Record an observed value for a metric. recordedAt defaults to now; pass it to backfill history.

Requires
metric:create
Input
  • metricId: uuid
  • value: number
  • recordedAt?: string
  • note?: string
  • source?: "manual" | "integration" | "system"
  • idempotencyKey?: stringStable key for safe retries — a repeat returns the first result instead of writing again.

create_strategy

Create a strategy (name plus a markdown content body). Starts as a draft.

Requires
strategy:create
Input
  • name: string
  • content: string
  • status?: "draft" | "active" | "archived"
  • idempotencyKey?: stringStable key for safe retries — a repeat returns the first result instead of writing again.

update_strategy

Update a strategy's name or content. An optional changeNote labels the version this edit creates.

Requires
strategy:update
Input
  • name?: string
  • content?: string
  • changeNote?: string
  • id: uuidStrategy UUID
  • idempotencyKey?: stringStable key for safe retries — a repeat returns the first result instead of writing again.

publish_strategy

Publish a draft strategy, making it the active version.

Requires
strategy:create
Input
  • id: uuidStrategy UUID
  • idempotencyKey?: stringStable key for safe retries — a repeat returns the first result instead of writing again.

archive_strategy

Archive a strategy, retiring it from the active set.

Requires
strategy:create
Input
  • id: uuidStrategy UUID
  • idempotencyKey?: stringStable key for safe retries — a repeat returns the first result instead of writing again.

create_vision

Create a vision: the company vision (kind 'company', one per org) or a product vision (kind 'product'). The narrative is the persuasive 2-5 year story, not a slogan.

Requires
vision:create
Input
  • name: string
  • kind?: "company" | "product"
  • narrative: string
  • idempotencyKey?: stringStable key for safe retries — a repeat returns the first result instead of writing again.

update_vision

Update a vision's name or narrative. Edits are versioned.

Requires
vision:update
Input
  • name?: string
  • narrative?: string
  • id: uuidVision UUID
  • idempotencyKey?: stringStable key for safe retries — a repeat returns the first result instead of writing again.

create_team

Create a team. The 2-4 character prefix becomes the team's task ref prefix (e.g. ENG-42).

Requires
team:create
Input
  • name: string
  • description?: string
  • prefix?: string
  • idempotencyKey?: stringStable key for safe retries — a repeat returns the first result instead of writing again.

update_team

Update a team's name, description, ref prefix, or default PRD template.

Requires
team:update
Input
  • name?: string
  • description?: string
  • defaultPrdTemplateId?: uuid | null
  • prefix?: string
  • id: uuidTeam UUID
  • idempotencyKey?: stringStable key for safe retries — a repeat returns the first result instead of writing again.

delete_team

Delete a team. Its opportunities/tasks/intake keep their rows (team unset, refs stable); memberships and team-scoped config are removed. Refuses to delete the org's only team.

Requires
team:delete
Input
  • id: uuidTeam UUID
  • idempotencyKey?: stringStable key for safe retries — a repeat returns the first result instead of writing again.

add_team_member

Add a user to a team's roster.

Requires
team:create
Input
  • teamId: uuid
  • userId: uuid
  • idempotencyKey?: stringStable key for safe retries — a repeat returns the first result instead of writing again.

remove_team_member

Remove a user from a team's roster.

Requires
team:delete
Input
  • teamId: uuidTeam UUID
  • userId: uuidUser UUID
  • idempotencyKey?: stringStable key for safe retries — a repeat returns the first result instead of writing again.

update_user

Update an org member's profile (name, weekly hours). Salary fields require a leadership role and travel as a pair; role changes need org:manage, which API keys never carry.

Requires
user:update
Input
  • name?: string
  • salaryMonthly?: number
  • salaryCurrency?: "USD" | "EUR" | "GBP" | "CAD" | "AUD" | "CHF" | "SEK" | "NOK" | "DKK" | "JPY"
  • weeklyHours?: integer
  • role?: "engineer" | "pm" | "em" | "executive" | "admin" | "sales" | "operations"
  • orgRoleId?: uuid | null
  • id: uuidUser UUID
  • idempotencyKey?: stringStable key for safe retries — a repeat returns the first result instead of writing again.

create_insight

Record a customer insight (one discrete signal, not a whole document). Requires a customerId; optionally set source, moscow priority, deadline, tagIds for clustering, and attach to an opportunity/task/objective.

Requires
insight:create
Input
  • customerId?: uuid | null
  • stakeholderUserId?: uuid | null
  • contactId?: uuid | null
  • verbatim: string
  • interpretation?: string | null
  • source: "interview" | "support" | "sales" | "async" | "observation" | "submission"
  • moscow?: "must" | "should" | "could" | "wont" | null
  • deadline?: string | null
  • attachToOpportunityId?: uuid
  • attachToTaskId?: uuid
  • attachToObjectiveId?: uuid
  • pinned?: boolean
  • tagIds?: string[]
  • idempotencyKey?: stringStable key for safe retries — a repeat returns the first result instead of writing again.

attach_insight

Link an existing insight to an opportunity, task, or objective (optionally pinned).

Requires
insight:update
Input
  • insightId: uuid
  • entityType: "opportunity" | "task" | "objective"
  • entityId: uuid
  • pinned?: boolean
  • idempotencyKey?: stringStable key for safe retries — a repeat returns the first result instead of writing again.

create_tag

Create a tag for clustering insights. List existing tags first (list_tags) and reuse them rather than minting duplicates.

Requires
tag:create
Input
  • name: string
  • themeId?: uuid | null
  • color?: "red" | "teal" | "orange" | "lime" | "green" | "cyan" | "blue" | "indigo" | "purple" | "neutral" | null
  • idempotencyKey?: stringStable key for safe retries — a repeat returns the first result instead of writing again.

mark_notification_read

Mark one of your notifications read.

Requires
none (self-scoped: your own data only)
Input
  • id: uuidNotification UUID
  • idempotencyKey?: stringStable key for safe retries — a repeat returns the first result instead of writing again.

mark_all_notifications_read

Mark all of your notifications read.

Requires
none (self-scoped: your own data only)
Input
  • idempotencyKey?: stringStable key for safe retries — a repeat returns the first result instead of writing again.

pin_notification

Pin one of your notifications so it stays at the top of the inbox.

Requires
none (self-scoped: your own data only)
Input
  • id: uuidNotification UUID
  • idempotencyKey?: stringStable key for safe retries — a repeat returns the first result instead of writing again.

unpin_notification

Unpin one of your notifications.

Requires
none (self-scoped: your own data only)
Input
  • id: uuidNotification UUID
  • idempotencyKey?: stringStable key for safe retries — a repeat returns the first result instead of writing again.

unsubscribe_notification

Unsubscribe from the entity a notification came from, muting its future notifications for you.

Requires
none (self-scoped: your own data only)
Input
  • id: uuidNotification UUID
  • idempotencyKey?: stringStable key for safe retries — a repeat returns the first result instead of writing again.