Tools reference

Every MCP tool maps to one or two REST calls. Reads return an envelope with coverage; writes take idempotency_key and reason and end as executed or pending_approval. Generated from apps/mcp and KONTRAKT §11 (v1.0.0).

Read tools

#get_ad_performance readcoming soon

Ad spend per platform: planned / reserved / actual, results and budget pacing.

Ads integrations (Google Ads, Meta Ads) are coming soon: the tool works against planned budgets and reservations; live spend arrives once an Ads integration is connected.

get_ad_performance(business_id, from?, to?, platform?) → {platform, spend: P/R/A, results?, pacing?}[]
Required scope
finances (observe)
Result
envelope { data, coverage, fx?, provenance_summary?, request_id }

#get_agent_economics read

Cost of an agent (agent_runtime events with agent_id + tool costs) vs. the impact of its reviewed decisions (revenue/net change), actions and approvals counts, cost per action and ROI, all with data_class; `unavailable` when nothing was reviewed yet. Without agent_id: your own economics.

get_agent_economics(agent_id?, business_id?, from?, to?) → AgentEconomics (0.7.1, spec §32; agent: tylko własne)
Required scope
finances (observe)
Result
envelope { data, coverage, fx?, provenance_summary?, request_id }

#get_available_capital read

Free capital = cash_known − committed_costs − reserved_budgets − upcoming_subscriptions over the horizon. Check before proposing any spend.

get_available_capital(Scope, horizon_days?=30) → FreeCapital (§6)
Required scope
finances (observe)
Result
envelope { data, coverage, fx?, provenance_summary?, request_id }

#get_business_context read

START HERE. One compact snapshot of a business (30d summary, free capital, forecast status, ads, top subscriptions, reservations, pending approvals, your passport, coverage). Replaces 6+ separate calls.

get_business_context(business_id) → {business, projects[], summary_30d, free_capital, forecast_status, subscriptions_top[], active_reservations[], pending_approvals[], my_passport,
Required scope
finances (observe)
Result
envelope { data, coverage, fx?, provenance_summary?, request_id }

#get_business_summary read

Financial summary of one business: revenue, costs (planned/reserved/actual kept separate), by category. Defaults to the current month.

get_business_summary(business_id, from?, to?) → FinancialSummary {revenue, costs, by_category[], planned/reserved/actual}
Required scope
finances (observe)
Result
envelope { data, coverage, fx?, provenance_summary?, request_id }

#get_cashflow_forecast read

Cash-flow forecast (7/30/90 days or custom range) with status list and reasons. Estimated components are labelled, never treat them as real data. Daily points are omitted unless include_points=true.

get_cashflow_forecast(Scope, horizon: 7|30|90 | {from,to}) → Forecast (§7)
Required scope
finances (observe)
Result
envelope { data, coverage, fx?, provenance_summary?, request_id }

#get_daily_brief read

Morning brief: last-24h highlights, alerts, upcoming costs (7d), approvals pending. Pass business_id unless you hold an `all` grant.

get_daily_brief(date?, business_id?) → {date, business_id|null, highlights: Highlight[] /* 1.0.0-rc.14, R-011, §16 */, alerts[], upcoming[], approvals_pending, goal_deviations[], agent_costs_30d: {agent_id, name, total_minor, currency}[] /* 0.7.1 */}
Required scope
finances (observe)
Result
envelope { data, coverage, fx?, provenance_summary?, request_id }

#get_decision_outcomes read

Decision journal review: for each recorded decision, actual vs expected after the horizon (variance, met) and before/after change of ad_spend, revenue, net, roas, all with data_class. Use it to learn from your own history.

get_decision_outcomes(Scope, since?, limit?=20) → DecisionOutcome[]        (0.5.2, §2.18; własne decyzje agenta)
Required scope
decisions (observe)
Result
envelope { data, coverage, fx?, provenance_summary?, request_id }

#get_decision_review read

Review one decision from your journal: expected vs actual per metric after the horizon (variance, met) and before/after change of ad_spend, revenue, net, roas, with data_class. Alias of get_decision_outcomes for a single decision_id.

get_decision_review(decision_id) → DecisionOutcome (§2.18; własne decyzje)
Required scope
decisions (observe)
Result
envelope { data, coverage, fx?, provenance_summary?, request_id }

#get_goals read

Goals in scope (business/project/personal + your own agent goals) with `progress` computed from the ledger: current_value, target, gap, on_track, data_class. Analyse against the goal, not just whether revenue grows.

get_goals(Scope, include_archived?=false) → (Goal & {progress})[]   (0.5.2, §2.17; tylko cele w zasięgu grantów)
Required scope
goals (observe; onboarding = własny passport, zawsze dozwolone)
Result
envelope { data, coverage, fx?, provenance_summary?, request_id }

#get_my_activity read

Your own audit trail: what you executed, what waits for approval, what was denied and why (with what_to_do), plus a summary (writes, denied, approvals by status, your cost in the last 30 days). Only your entries; use it to review yesterday before acting today.

Required scope
finances (observe)
Result
envelope { data, coverage, fx?, provenance_summary?, request_id }

#get_onboarding read

Call once after connecting (and again if your passport changes): what Operlance is, your grants and limits in short form, which businesses you can access, what is forbidden, goals, key concepts and 3 example tool sequences for your scopes. Optional `resource` narrows it to one business/project/personal.

get_onboarding(resource?) → Onboarding (0.5.2, spec §21; 0.6.1: `resource` = `business:<id>` | `project:<id>` zawęża sekcje do jednego zasobu) + MCP resource `operlance://onboarding` (markdown ≤ 2 000 tokenów, bez sekretów):
Required scope
goals (observe; onboarding = własny passport, zawsze dozwolone)
Result
envelope { data, coverage, fx?, provenance_summary?, request_id }

#get_personal_forecast read

Cash-flow forecast of the personal workspace (7/30/90 days or from/to) with statuses and reasons. Requires an explicit @personal grant. Daily points omitted unless include_points=true.

get_personal_forecast(horizon?: 7|30|90 | {from,to}, include_points?) → Forecast dla personal (jak wyżej)
Required scope
finances (observe)
Result
envelope { data, coverage, fx?, provenance_summary?, request_id }

#get_personal_summary read

Owner's personal finances (living costs, private subscriptions, planned spend): summary for a period with planned/reserved/actual and coverage. Requires an explicit @personal grant.

get_personal_summary(from?, to?) → FinancialSummary dla personal (scope `finances:observe@personal`; brak grantu → scope_denied z what_to_do „poproś o grant @personal")
Required scope
finances (observe)
Result
envelope { data, coverage, fx?, provenance_summary?, request_id }

#get_project_health read

Health of a business and its projects as a list of statuses with reasons (no scores).

get_project_health(business_id, project_id?) → {status[], reasons[]} (bez score)
Required scope
finances (observe)
Result
envelope { data, coverage, fx?, provenance_summary?, request_id }

#get_subscription_capacity read

Seats/slots of shared subscriptions: total, used, free, utilization, usage per workspace. Check for free capacity before proposing a new tool.

get_subscription_capacity(subscription_id? | Scope) → {subscription_id, unit, total, used, free, utilization, by_workspace[]}[]
Required scope
finances (observe)
Result
envelope { data, coverage, fx?, provenance_summary?, request_id }

#get_subscriptions read

Subscriptions in scope with cost allocations between businesses and capacity.

get_subscriptions(Scope, status?) → Subscription[] (z alokacjami)
Required scope
finances (observe)
Result
envelope { data, coverage, fx?, provenance_summary?, request_id }

#plan_capacity read

Before buying a tool: check whether existing shared subscriptions have free capacity for each need (unit, amount, business). Returns covered_by / shortfall / suggestion (allocate | upgrade | buy_new) with an estimated cost. With idempotency_key + reason it also proposes the cost re-allocation as an Approval (allocate_shared_expense); it never buys anything.

plan_capacity(needs: {unit, amount, business_id}[1..10], W?) → { per_need: { need, covered_by: {subscription_id, free}|null, shortfall: int, suggestion: "allocate"|"upgrade"|"buy_new", est_cost_minor: int|null, proposed_allocations[] }[], proposals: Approval[] /* tylko z W: Approval allocate_shared_expense, nic nie kupuje */ }
Required scope
-
Result
envelope { data, coverage, fx?, provenance_summary?, request_id }

#simulate_scenario read

What-if sandbox: apply changes (add expense/recurring/revenue, cancel subscription, change a DAILY ad budget) to a copy of the ledger and compare forecast, runway, free capital and break-even against the baseline. Nothing is saved. Use before propose_ad_budget_change or reserve_budget.

simulate_scenario(Scope, horizon_days?=7|30|90, changes: Change[1..20]) → Simulation        // READ (finances:observe + forecast:observe); bez zapisu, bez Idempotency-Key
Required scope
finances + forecast (observe)
Result
envelope { data, coverage, fx?, provenance_summary?, request_id }

Write tools

Common parameters W = { idempotency_key, reason }; Scope = { business_id?, project_id?, personal? }; Money = { amount_minor, currency }.

#allocate_shared_expense write

Replace the cost split of a shared subscription. Full list, share_bps must sum to 10000.

allocate_shared_expense(W, subscription_id, allocations: {Scope, method, share_bps}[]) → SubscriptionAllocation[]
Required scope
allocations
Result
201 executed · 202 pending_approval (level recommend or per-action limit exceeded)

#create_decision write

Decision journal: record what you are about to do, why, the expected impact (expected[] = measurable targets with horizon) and the data behind it, before a significant action. Reviewed against actuals after the horizon via get_decision_outcomes.

create_decision(W, Scope, title, rationale, expected_impact?, expected?[], action_ref?, refs?[]) → FinancialEvent(agent_action) (§2.18)
Required scope
decisions
Result
201 executed · 202 pending_approval (level recommend or per-action limit exceeded)

#create_expense write

Record an ACTUAL one-off expense. Link reservation_id / fulfills_event_id when it consumes a reservation or fulfils a planned expense.

create_expense(W, Scope, Money, occurred_on, category, description) → FinancialEvent
Required scope
expenses
Result
201 executed · 202 pending_approval (level recommend or per-action limit exceeded)

#create_planned_expense write

Record a PLANNED one-off expense (intent). It enters forecasts and committed costs; it is never overwritten by the actual.

create_planned_expense(W, Scope, Money, expected_on, category, description) → FinancialEvent
Required scope
planned_expenses
Result
201 executed · 202 pending_approval (level recommend or per-action limit exceeded)

#create_recurring_expense write

Create a subscription (recurring cost). Use either a single Scope or allocations[] (share_bps sum = 10000) for shared costs. category defaults to saas; pick contractors, legal_admin, infrastructure etc. when it is not software, and remember it only applies to charges from now on.

create_recurring_expense(W, Scope|allocations[], name, vendor, Money, billing_interval, next_charge_on, category?, plan?, capacity?[]) → Subscription   // rc.38: category? z CATEGORIES (§2.6), domyślnie saas, tylko przyszłe obciążenia
Required scope
subscriptions + expenses
Result
201 executed · 202 pending_approval (level recommend or per-action limit exceeded)

#propose_ad_budget_change writecoming soon

Propose changing a DAILY ad budget. Always leaves an Approval record. With ads:execute and within max_change_pct_per_day it is recorded at once as a budget plan in Operlance; the budget in the ad platform changes only after the owner approves it in the Approval Center and ad writes are turned on for that integration, otherwise the approval is kept as a plan (platform_applied=false). The result carries `simulation` (impact on forecast/runway/free capital) so you and the owner see the effect before it lands.

Ads integrations (Google Ads, Meta Ads) are coming soon: the tool works against planned budgets and reservations; live spend arrives once an Ads integration is connected.

propose_ad_budget_change(W, business_id, platform, campaign_id?, current: Money, proposed: Money) → Approval (zawsze; execute w MVP = zapis planu, bez wywołań platform)
Required scope
ads (≥recommend)
Result
201 executed · 202 pending_approval (level recommend or per-action limit exceeded)

#propose_goal write

Propose a goal (business goal or your own agent constraint, e.g. max ads spend). Always ends as pending_approval, the owner decides. Money targets in minor units.

propose_goal(W, Scope, metric, comparator, target_value, currency?, title?, deadline?, agent_id?) → Approval (zawsze 202, action=create_goal; §2.17)
Required scope
goals (≥ recommend, wynik zawsze Approval)
Result
201 executed · 202 pending_approval (level recommend or per-action limit exceeded)

#request_data write

Ask the owner for missing data or an integration (e.g. 'Stripe is not connected, I cannot compute revenue'). Always ends as pending_approval; the owner sees it in the Approval Center with a one-click action.

request_data(W, kind: "connect_integration"|"provide_data", details: { provider? /* connect */, field? /* provide, np. cash_snapshot */, why, impact_on_coverage? }) → Approval (202, action = kind)
Required scope
-
Result
201 executed · 202 pending_approval (level recommend or per-action limit exceeded)

#request_permission write

Ask the owner for a missing grant (scope × level × resource). Always ends as pending_approval; explain in `reason` what analysis is blocked without it.

request_permission(W, scope, level, resource, limits?, expires_at?) → Approval
Required scope
-
Result
201 executed · 202 pending_approval (level recommend or per-action limit exceeded)

#reserve_budget write

Block capital for a purpose and period (BudgetReservation). Reduces free capital until period_end or release.

reserve_budget(W, Scope, Money, purpose, category, period_start, period_end, platform?) → BudgetReservation   // rc.36: platform?: google_ads|meta_ads, tylko przy category=ads, przypina rezerwację do platformy jak REST 0.4.2; bez niego rezerwacja nieprzypisana
Required scope
budget_reserve
Result
201 executed · 202 pending_approval (level recommend or per-action limit exceeded)

All signatures

// READ
get_business_summary(business_id, from?, to?)            → FinancialSummary {revenue, costs, by_category[], planned/reserved/actual}
get_business_context(business_id)                        → {business, projects[], summary_30d, free_capital, forecast_status, subscriptions_top[], active_reservations[], pending_approvals[], my_passport,
                                                              recent_decisions: {id, title, decided_on, review: {status, met}, expected[], observed[]}[] /* 0.7.0, ≤5 */,
                                                              agent_costs_30d: {agent_id, name, total_minor, currency}[] /* 0.7.1 */, other_agents_activity: {agent_id, name, action, at, outcome}[] /* 0.7.1: 24 h, max 10, spec §31 */,
                                                              anomalies: {code: "revenue_drop"|"cost_spike"|"no_revenue_14d", message, amount_minor?, data_class}[] /* 0.7.0: revenue 7d vs poprzednie 7d < -30 %; koszt dnia > 3× mediana 30 d w kategorii; brak przychodu 14 dni */ }
get_available_capital(Scope, horizon_days?=30)           → FreeCapital (§6)
get_subscriptions(Scope, status?)                        → Subscription[] (z alokacjami)
get_subscription_capacity(subscription_id? | Scope)      → {subscription_id, unit, total, used, free, utilization, by_workspace[]}[]
get_ad_performance(business_id, from?, to?, platform?)   → {platform, spend: P/R/A, results?, pacing?}[]
get_cashflow_forecast(Scope, horizon: 7|30|90 | {from,to}) → Forecast (§7)
get_project_health(business_id, project_id?)             → {status[], reasons[]} (bez score)
get_daily_brief(date?, business_id?)                     → {date, business_id|null, highlights: Highlight[] /* 1.0.0-rc.14, R-011, §16 */, alerts[], upcoming[], approvals_pending, goal_deviations[], agent_costs_30d: {agent_id, name, total_minor, currency}[] /* 0.7.1 */}
get_goals(Scope, include_archived?=false)                → (Goal & {progress})[]   (0.5.2, §2.17; tylko cele w zasięgu grantów)
get_decision_outcomes(Scope, since?, limit?=20)          → DecisionOutcome[]        (0.5.2, §2.18; własne decyzje agenta)
get_onboarding(resource?)                                → Onboarding (0.5.2, spec §21; 0.6.1: `resource` = `business:<id>` | `project:<id>` zawęża sekcje do jednego zasobu) + MCP resource `operlance://onboarding` (markdown ≤ 2 000 tokenów, bez sekretów):
  Onboarding = { about: string, my_passport: { agent: {id, name, kind}, grants: string[] /* "ads:execute@business:<id> ≤ 500 PLN/akcja, 10%/dzień" */, expires_at, token_expires_at },
    businesses_accessible: {id, name, currency}[], forbidden: string[], limits: PassportGrant["limits"][], goals: Goal[],
    concepts: { planned_vs_actual, reservations, decision_journal, approvals }: Record<string,string>, examples: {title, tools: string[]}[] }

// WRITE (W wymagane)
create_expense(W, Scope, Money, occurred_on, category, description)                         → FinancialEvent
create_recurring_expense(W, Scope|allocations[], name, vendor, Money, billing_interval, next_charge_on, category?, plan?, capacity?[]) → Subscription   // rc.38: category? z CATEGORIES (§2.6), domyślnie saas, tylko przyszłe obciążenia
create_planned_expense(W, Scope, Money, expected_on, category, description)                → FinancialEvent
reserve_budget(W, Scope, Money, purpose, category, period_start, period_end, platform?)    → BudgetReservation   // rc.36: platform?: google_ads|meta_ads, tylko przy category=ads, przypina rezerwację do platformy jak REST 0.4.2; bez niego rezerwacja nieprzypisana
allocate_shared_expense(W, subscription_id, allocations: {Scope, method, share_bps}[])     → SubscriptionAllocation[]
propose_ad_budget_change(W, business_id, platform, campaign_id?, current: Money, proposed: Money) → Approval (zawsze; execute w MVP = zapis planu, bez wywołań platform)
request_permission(W, scope, level, resource, limits?, expires_at?)                        → Approval
create_decision(W, Scope, title, rationale, expected_impact?, expected?[], action_ref?, refs?[]) → FinancialEvent(agent_action) (§2.18)
propose_goal(W, Scope, metric, comparator, target_value, currency?, title?, deadline?, agent_id?) → Approval (zawsze 202, action=create_goal; §2.17)

// 0.7.0 (spec §29 What-if, §25 Agent Requests, §28 review)
simulate_scenario(Scope, horizon_days?=7|30|90, changes: Change[1..20]) → Simulation        // READ (finances:observe + forecast:observe); bez zapisu, bez Idempotency-Key
  Change = { kind: "add_expense", amount: Money, on?, category?, label? } | { kind: "add_recurring", amount: Money, billing_interval, interval_count?=1, next_charge_on?, label? }
         | { kind: "cancel_subscription", subscription_id } | { kind: "change_ad_budget", platform, current: Money, proposed: Money /* budżety DZIENNE */, campaign_id?, from? } | { kind: "add_revenue", amount: Money, on?, label? }
  ForecastSnapshot = { closing_cash_minor, min_cash_minor, net_minor, projected_in_minor, projected_out_minor, runway_days|null, free_capital_minor, break_even_on|null, statuses[] }
  Simulation = { scope, currency, horizon_days, from, to, changes[], baseline: ForecastSnapshot, simulated: ForecastSnapshot,
                 delta: { closing_cash_minor, min_cash_minor, net_minor, runway_days|null, free_capital_minor, break_even_on: {from, to} },
                 unmodeled_minor /* część zmiany nieodwzorowana eventami, odjęta analitycznie */, data_class: "estimated", notes: string[] }
request_data(W, kind: "connect_integration"|"provide_data", details: { provider? /* connect */, field? /* provide, np. cash_snapshot */, why, impact_on_coverage? }) → Approval (202, action = kind)
get_decision_review(decision_id)                          → DecisionOutcome (§2.18; własne decyzje)
get_agent_economics(agent_id?, business_id?, from?, to?)  → AgentEconomics (0.7.1, spec §32; agent: tylko własne)

// 1.0.0-rc.2 (Agentowy 66), experimental
get_personal_summary(from?, to?)                          → FinancialSummary dla personal (scope `finances:observe@personal`; brak grantu → scope_denied z what_to_do „poproś o grant @personal")
get_personal_forecast(horizon?: 7|30|90 | {from,to}, include_points?) → Forecast dla personal (jak wyżej)
plan_capacity(needs: {unit, amount, business_id}[1..10], W?) → { per_need: { need, covered_by: {subscription_id, free}|null, shortfall: int, suggestion: "allocate"|"upgrade"|"buy_new", est_cost_minor: int|null, proposed_allocations[] }[], proposals: Approval[] /* tylko z W: Approval allocate_shared_expense, nic nie kupuje */ }
  AgentEconomics = { agent_id, period: {from,to}, currency, cost: { total_minor, runtime_minor /* agent_runtime z ledgera */, tools_minor /* alokacje subskrypcji z SubscriptionAllocation.agent_id */, by_category[] },
                     actions: { total, by_tool: Record<string,int>, denied: int }, approvals: { proposed, approved, rejected, expired, pending },
                     impact: { reviewed_decisions, met, missed, revenue_generated_minor|null, cost_saved_minor|null, data_class },
                     roi: { value: number|null, data_class: "estimated"|"unavailable", note } }
Source: KONTRAKT §11 (Polish, verbatim from the contract)

#11. MCP tools (spec §19), sygnatury

Serwer operlance, auth tokenem agenta. Każdy wynik: { data, coverage, provenance_summary?, request_id }. Błąd tool (0.6.1): isError: true + { code, message, what_to_do, retry_after_seconds?, details?, request_id } (code = kod z §9; what_to_do = jedno zdanie po angielsku, co agent ma zrobić, bez sekretów). Każdy write: parametry wspólne W = { idempotency_key: string, reason: string }; wynik { status: "executed"|"pending_approval", data?, approval_id? }. Błędy = kody z §9. Scope = { business_id?: string, project_id?: string, personal?: boolean }; Money = { amount_minor: int, currency: string }.

// READ
get_business_summary(business_id, from?, to?)            → FinancialSummary {revenue, costs, by_category[], planned/reserved/actual}
get_business_context(business_id)                        → {business, projects[], summary_30d, free_capital, forecast_status, subscriptions_top[], active_reservations[], pending_approvals[], my_passport,
                                                              recent_decisions: {id, title, decided_on, review: {status, met}, expected[], observed[]}[] /* 0.7.0, ≤5 */,
                                                              agent_costs_30d: {agent_id, name, total_minor, currency}[] /* 0.7.1 */, other_agents_activity: {agent_id, name, action, at, outcome}[] /* 0.7.1: 24 h, max 10, spec §31 */,
                                                              anomalies: {code: "revenue_drop"|"cost_spike"|"no_revenue_14d", message, amount_minor?, data_class}[] /* 0.7.0: revenue 7d vs poprzednie 7d < -30 %; koszt dnia > 3× mediana 30 d w kategorii; brak przychodu 14 dni */ }
get_available_capital(Scope, horizon_days?=30)           → FreeCapital (§6)
get_subscriptions(Scope, status?)                        → Subscription[] (z alokacjami)
get_subscription_capacity(subscription_id? | Scope)      → {subscription_id, unit, total, used, free, utilization, by_workspace[]}[]
get_ad_performance(business_id, from?, to?, platform?)   → {platform, spend: P/R/A, results?, pacing?}[]
get_cashflow_forecast(Scope, horizon: 7|30|90 | {from,to}) → Forecast (§7)
get_project_health(business_id, project_id?)             → {status[], reasons[]} (bez score)
get_daily_brief(date?, business_id?)                     → {date, business_id|null, highlights: Highlight[] /* 1.0.0-rc.14, R-011, §16 */, alerts[], upcoming[], approvals_pending, goal_deviations[], agent_costs_30d: {agent_id, name, total_minor, currency}[] /* 0.7.1 */}
get_goals(Scope, include_archived?=false)                → (Goal & {progress})[]   (0.5.2, §2.17; tylko cele w zasięgu grantów)
get_decision_outcomes(Scope, since?, limit?=20)          → DecisionOutcome[]        (0.5.2, §2.18; własne decyzje agenta)
get_onboarding(resource?)                                → Onboarding (0.5.2, spec §21; 0.6.1: `resource` = `business:<id>` | `project:<id>` zawęża sekcje do jednego zasobu) + MCP resource `operlance://onboarding` (markdown ≤ 2 000 tokenów, bez sekretów):
  Onboarding = { about: string, my_passport: { agent: {id, name, kind}, grants: string[] /* "ads:execute@business:<id> ≤ 500 PLN/akcja, 10%/dzień" */, expires_at, token_expires_at },
    businesses_accessible: {id, name, currency}[], forbidden: string[], limits: PassportGrant["limits"][], goals: Goal[],
    concepts: { planned_vs_actual, reservations, decision_journal, approvals }: Record<string,string>, examples: {title, tools: string[]}[] }

// WRITE (W wymagane)
create_expense(W, Scope, Money, occurred_on, category, description)                         → FinancialEvent
create_recurring_expense(W, Scope|allocations[], name, vendor, Money, billing_interval, next_charge_on, category?, plan?, capacity?[]) → Subscription   // rc.38: category? z CATEGORIES (§2.6), domyślnie saas, tylko przyszłe obciążenia
create_planned_expense(W, Scope, Money, expected_on, category, description)                → FinancialEvent
reserve_budget(W, Scope, Money, purpose, category, period_start, period_end, platform?)    → BudgetReservation   // rc.36: platform?: google_ads|meta_ads, tylko przy category=ads, przypina rezerwację do platformy jak REST 0.4.2; bez niego rezerwacja nieprzypisana
allocate_shared_expense(W, subscription_id, allocations: {Scope, method, share_bps}[])     → SubscriptionAllocation[]
propose_ad_budget_change(W, business_id, platform, campaign_id?, current: Money, proposed: Money) → Approval (zawsze; execute w MVP = zapis planu, bez wywołań platform)
request_permission(W, scope, level, resource, limits?, expires_at?)                        → Approval
create_decision(W, Scope, title, rationale, expected_impact?, expected?[], action_ref?, refs?[]) → FinancialEvent(agent_action) (§2.18)
propose_goal(W, Scope, metric, comparator, target_value, currency?, title?, deadline?, agent_id?) → Approval (zawsze 202, action=create_goal; §2.17)

// 0.7.0 (spec §29 What-if, §25 Agent Requests, §28 review)
simulate_scenario(Scope, horizon_days?=7|30|90, changes: Change[1..20]) → Simulation        // READ (finances:observe + forecast:observe); bez zapisu, bez Idempotency-Key
  Change = { kind: "add_expense", amount: Money, on?, category?, label? } | { kind: "add_recurring", amount: Money, billing_interval, interval_count?=1, next_charge_on?, label? }
         | { kind: "cancel_subscription", subscription_id } | { kind: "change_ad_budget", platform, current: Money, proposed: Money /* budżety DZIENNE */, campaign_id?, from? } | { kind: "add_revenue", amount: Money, on?, label? }
  ForecastSnapshot = { closing_cash_minor, min_cash_minor, net_minor, projected_in_minor, projected_out_minor, runway_days|null, free_capital_minor, break_even_on|null, statuses[] }
  Simulation = { scope, currency, horizon_days, from, to, changes[], baseline: ForecastSnapshot, simulated: ForecastSnapshot,
                 delta: { closing_cash_minor, min_cash_minor, net_minor, runway_days|null, free_capital_minor, break_even_on: {from, to} },
                 unmodeled_minor /* część zmiany nieodwzorowana eventami, odjęta analitycznie */, data_class: "estimated", notes: string[] }
request_data(W, kind: "connect_integration"|"provide_data", details: { provider? /* connect */, field? /* provide, np. cash_snapshot */, why, impact_on_coverage? }) → Approval (202, action = kind)
get_decision_review(decision_id)                          → DecisionOutcome (§2.18; własne decyzje)
get_agent_economics(agent_id?, business_id?, from?, to?)  → AgentEconomics (0.7.1, spec §32; agent: tylko własne)

// 1.0.0-rc.2 (Agentowy 66), experimental
get_personal_summary(from?, to?)                          → FinancialSummary dla personal (scope `finances:observe@personal`; brak grantu → scope_denied z what_to_do „poproś o grant @personal")
get_personal_forecast(horizon?: 7|30|90 | {from,to}, include_points?) → Forecast dla personal (jak wyżej)
plan_capacity(needs: {unit, amount, business_id}[1..10], W?) → { per_need: { need, covered_by: {subscription_id, free}|null, shortfall: int, suggestion: "allocate"|"upgrade"|"buy_new", est_cost_minor: int|null, proposed_allocations[] }[], proposals: Approval[] /* tylko z W: Approval allocate_shared_expense, nic nie kupuje */ }
  AgentEconomics = { agent_id, period: {from,to}, currency, cost: { total_minor, runtime_minor /* agent_runtime z ledgera */, tools_minor /* alokacje subskrypcji z SubscriptionAllocation.agent_id */, by_category[] },
                     actions: { total, by_tool: Record<string,int>, denied: int }, approvals: { proposed, approved, rejected, expired, pending },
                     impact: { reviewed_decisions, met, missed, revenue_generated_minor|null, cost_saved_minor|null, data_class },
                     roi: { value: number|null, data_class: "estimated"|"unavailable", note } }

#Transport zdalny MCP (1.0.0-rc.2, Zadanie 81/84)

  • Dla użytkowników dostępny jest wyłącznie Streamable HTTP (https://mcp.operlance.app/mcp, nagłówek Authorization: Bearer agt_…). Transport stdio (paczka @operlance/mcp, token w env OPERLANCE_TOKEN) działa dziś tylko z repozytorium (dev), bo paczka nie jest opublikowana w npm (private); publikacja czeka na decyzję Właściciela (backlog, Rozwój). Wskazówki błędów MCP (what_to_do dla unauthenticated, token_expired, api_unreachable) mówią o nagłówku Authorization w konfiguracji klienta, nie o zmiennych env (przez HTTP ich nie ma).
  • Ten sam serwer operlance działa po stdio (wyżej) i po Streamable HTTP: POST <MCP_PUBLIC_URL>/mcp z nagłówkiem Authorization: Bearer agt_… per żądanie (stateless: brak sesji MCP, sessionIdGenerator wyłączony, odpowiedzi JSON), GET /health → {status:"ok", transport:"streamable-http", contract}; żadnych tokenów w env usługi. Staging: usługa mcp (Railway, apps/mcp/Dockerfile.mcp, port 8788) rozmawia z API przez sieć prywatną (OPERLANCE_API_URL=http://api.railway.internal:4000); adres publiczny https://mcp.operlance.app (custom domain Railway, Q20, PRODUCTION §2).
  • Auth i uprawnienia identyczne jak REST (token agenta → Passport; allowed_cidrs widzą IP klienta z X-Forwarded-For); rate limit per token 120/min (na stagingu w pamięci procesu, docelowo rate_limits w DB); brak tokenu/zły token → HTTP 401 z {code:"unauthenticated"}; przekroczenie → 429 + Retry-After; błędy tool jak §11 (isError, what_to_do). CORS wyłączony (klienci MCP to procesy, nie przeglądarki); nagłówki z SECURITY §3; limit body 1 MB.
  • Klienci: claude mcp add --transport http operlance <MCP_PUBLIC_URL>/mcp --header "Authorization: Bearer …"; Claude Desktop/Cursor: { "url": "…/mcp", "headers": { "Authorization": "Bearer …" } } (token wklejany przez Ownera z panelu, nigdy w repo/docs).
Source: KONTRAKT §10 (required scopes) (Polish, verbatim from the contract)

#10. Scopes i poziomy autonomii (spec §22-23)

Scopes (string): finances, subscriptions, ads, forecast, expenses, planned_expenses, budget_reserve, allocations, goals, decisions. Grant = scope × level × resource (all | business:<id> | project:<id> | personal) × limits. Zapis skrótowy: ads:execute@business:<id>.

levelznaczenie
observeodczyt, analiza, raport
recommendobserve + write tworzy Approval (202), nic nie jest wykonywane
executerecommend + write wykonywany od razu w limitach; ponad limit „per action" → Approval; ponad limit dzienny/miesięczny → 403 limit_exceeded
  • Konflikt zasobu między agentami (0.7.1, spec §31): write/propozycja dotycząca tego samego zasobu (kampania, subskrypcja, rezerwacja), dla którego istnieje pending Approval innego agenta → 409 state_conflict z details.conflict {approval_id, agent_id, action} (AuditEntry denied, AuthzDecision.deny.code += state_conflict). Check konfliktu i INSERT Approval wykonywane w jednej transakcji (authorize dostaje tx), żeby dwie równoległe propozycje nie przeszły obie.
  • Limity dzienne/miesięczne liczą także kwoty OCZEKUJĄCYCH Approval agenta (status=pending), ochrona przed podwójnym budżetowaniem (spec §31): usage = Σ wykonane akcje w oknie + Σ pending Approval z amount_minor w oknie; po rejected/expired/cancelled kwota wraca do puli; approved przechodzi z „pending” do „wykonane” bez podwójnego liczenia. limit_checked w AuditEntry rozbija usage na executed_minor i pending_minor. Odmowa limit_exceeded (0.6.1): AuditEntry denied z limit_checked[] (jeden wpis per sprawdzony limit: {limit_key, limit_minor|pct, usage_minor, attempted_minor, currency, window}).
  • Samoobsługowe dla każdego aktywnego agenta (zamierzone, 0.8.1): GET /me, GET /agents/me, GET /agents/:id (własny), GET /agents/:id/passport (własny), POST /permission-requests, POST /data-requests, GET /approvals (własne): nie wymagają grantu, bo agent musi móc poznać własne uprawnienia i poprosić o więcej; zawsze audytowane.
  • Default deny. Brak grantu = 403 scope_denied (z audit denied). personal nigdy nie wynika z all, wymaga jawnego grantu @personal.
  • Grant na business obejmuje jego projekty. Grant wygasły (expires_at) = brak grantu.
  • ads:execute limit max_change_pct_per_day (np. 10 = ±10%).
  • Wymagany scope: czytanie podsumowań finances:observe; create_expense→expenses; create_recurring_expense→subscriptions+expenses; create_planned_expense→planned_expenses; reserve_budget→budget_reserve; allocate_shared_expense→allocations; propose_ad_budget_change→ads (≥recommend); create_decision→decisions; get_decision_outcomes, get_decision_review→decisions (observe); simulate_scenario→finances+forecast (observe); request_data→dowolny aktywny agent (jak request_permission); get_goals, get_onboarding→goals (observe; onboarding = własny passport, zawsze dozwolone); propose_goal→goals (≥ recommend, wynik zawsze Approval).