Getting started with the Data Studio
The Data Studio, part of the Trust Infrastructure Platform, serves trusted data as skills: governed, versioned capabilities you call by name. Every skill is reachable over MCP (Model Context Protocol, the open standard AI clients use to call tools). This guide takes you from “I have a key” to your first property answer. You can paste it into Claude Code or your own agent.
The full reference is the Developer Portal for your environment (the table in section 1). It has
the API Reference tab, plus the machine-readable OpenAPI description at /api/openapi.json on the
API host (send your key). When this guide and the portal disagree, the portal wins.
Two companions go deeper: the Consumer reference (who sees which skills, dictionary
coverage and proposals, property-scout, re-keying, troubleshooting) and Authoring skills.
For an agent, connect-claude-code.md gives the same connection steps as plain
Markdown, with this environment’s addresses filled in.
How the Data Studio is organized: the ontology
The Data Studio’s ontology is why an answer means the same thing whichever source produced it. Learn it first; everything you call hangs off it.
- Terms. Each ecosystem keeps a data dictionary of canonical terms (the parcel identifier,
assessed value, owner, and so on), each with a definition, a type and a stable identifier.
portal-browse-dictionarylists them. - Harmonization. Many upstream sources map onto one term. A city’s field and a provincial
assessor’s field that both mean “assessed value” arrive in the same place, so you parse one
shape, not one per source.
portal-dictionary-coverageshows how much of each source is mapped. - Sections. Terms are grouped into the answer’s sections (
Address,Property,Valuation,TaxHistoryand the rest; section 8). A term can also be tagged against an outside dictionary (in the real-estate ecosystem, RESO), but the ecosystem’s own dictionary is the core: it is what every answer is built from. - Skills anchored to terms. Skills declare the terms they take and return, so you can ask the
ontology rather than guess:
find-skills-for-conceptanswers “I have a parcel ID; what can I get with it?” - Versioned. The dictionary is published as releases you can pin and diff
(
portal-dictionary-release), with a naming standard. Propose a missing term withportal-dictionary-propose. - Traceable. Every value carries its provenance: which source produced it, and when (section 8).
Build your code and prompts against the terms, not against any one source. When the Data Studio adds or swaps a source behind a term, your parsing is designed not to change.
1. What you need
- A Data Studio API key, from your Data Studio contact. Keep it in an environment variable,
TIP_API_KEYbelow. Never commit it and never paste it into a prompt. - Your ecosystem slug (the Data Studio calls it the tenant). The examples use
real-estate. Older URLs that usecornerstonestill work as an alias.
Which host: the key’s prefix decides
| Environment | MCP host | Key prefix | Developer Portal |
|---|---|---|---|
| Dev | api.dev.trustinfrastructure.com | tip_dev_ | developer.dev.trustinfrastructure.com/portal |
| Staging | api.staging.trustinfrastructure.com | tip_stg_ | developer.staging.trustinfrastructure.com/portal |
| Production | api.trustinfrastructure.com | tip_ | developer.trustinfrastructure.com/portal |
Your Data Studio contact gives you a key for one of them; use the host that matches its prefix. A key sent to another environment’s host fails authentication. The examples below use dev.
Production access is by arrangement. Your Data Studio contact confirms the production host when your key is issued.
2. Connect
The endpoint, with the two settings this guide recommends:
https://api.dev.trustinfrastructure.com/api/mcp/real-estate?surface=lean&lane=consumer
surface=lean: a small tool set built for discovery (19 tools for a consumer key today). You find skills with the discovery tools and run any of them throughtip-invoke-skill.lane=consumer: on every request, the Data Studio checks that the key is read-only. A key that can write is refused before anything runs (HTTP 403,key_family_not_accepted).- An unknown value for either setting is refused with HTTP 400. The Data Studio never ignores it silently.
Claude Code
User scope (every project on your machine):
claude mcp add --transport http --scope user tip \
"https://api.dev.trustinfrastructure.com/api/mcp/real-estate?surface=lean&lane=consumer" \
--header "Authorization: Bearer $TIP_API_KEY"
Your shell expands $TIP_API_KEY when you run this, so the key is stored in ~/.claude.json, your
own file.
Project scope (shared through the repo): add a .mcp.json at the project root. Claude Code
expands ${TIP_API_KEY} from your environment, so no key is committed:
{
"mcpServers": {
"tip": {
"type": "http",
"url": "https://api.dev.trustinfrastructure.com/api/mcp/real-estate?surface=lean&lane=consumer",
"headers": { "Authorization": "Bearer ${TIP_API_KEY}" }
}
}
}
Where servers live: ~/.claude.json holds the user and local scopes, and .mcp.json holds the
project scope. MCP servers do not go in settings.json. When one name is defined in several
places, local wins over project, and project wins over user. To verify, run /mcp in a session
or claude mcp list.
Any other MCP client
Use the Streamable HTTP transport, the URL above, and one header:
Authorization: Bearer <your key>. If you call it over raw HTTP, POST JSON-RPC 2.0 and send
Accept: application/json, text/event-stream. Replies are SSE-framed (event: message then
data: {...}).
curl -sS -X POST "https://api.dev.trustinfrastructure.com/api/mcp/real-estate?surface=lean&lane=consumer" \
-H "Authorization: Bearer $TIP_API_KEY" \
-H "Content-Type: application/json" -H "Accept: application/json, text/event-stream" \
-d '{"jsonrpc":"2.0","method":"tools/list","id":1}'
The full surface, if you want it
Drop surface=lean and every published skill and data source becomes its own tool: about 925
tools, roughly 1.7 MB of tool definitions, for a consumer key on dev today. Use it when your
client copes with a large tool list and you want to call skills directly by tool name. Some tools
exist only on the full surface: portal-browse-skills (catalog browsing with category, q,
tier and jurisdiction filters), portal-browse-catalog (the data-source catalog), the
template tools, query-harmonized-data and the per-source ds-* tools.
3. Check your key
tip-key-introspecttakes no arguments. It describes the key making the call: its family (consumer,authororadmin), its role slugs, the write capabilities it holds after the deny list, and whether the consumer lane accepts it. It only ever describes that key, and it never returns key material.- The health check is a cheap test before you wire the key in:
curl -sS -H "Authorization: Bearer $TIP_API_KEY" \
"https://api.dev.trustinfrastructure.com/api/mcp/real-estate/health?lane=consumer&surface=lean"
# {"ok":true,"tenantId":"real-estate","appId":"…","surface":"lean","lane":"consumer"}
A 403 means the consumer lane does not accept this key. The refusal names tip-key-introspect;
call it without lane=consumer to see why.
4. Discover what your key can call
Start in plain language, for example “What property skills can I use on the Data Studio?”. These are the lean-surface tools as served:
| Tool | What it does |
|---|---|
discover-skills | Search published skills by category, domain or query. Pass compact=true with a query to get a short ranked list. Every record carries cost, pricing and authRequired. |
get-skills-manifest | The skills manifest, 25 per page (100 at most). Pass summary=true for slug, name, category, tool name, price and authRequired only. |
find-skills-for-concept | Skills linked to one dictionary concept. |
discover-use-cases | Ecosystem-defined use cases (named data requests). |
tip-list-skill-family | No arguments: the families. With family_id (for example property): their members. |
portal-browse-dictionary | The ecosystem dictionary: terms, definitions, types, and which sources provide each term. Filter by geography or tags. |
portal-dictionary-release | The dictionary as a versioned artifact you can pin and diff. |
portal-dictionary-coverage | How much of each published source is mapped to the dictionary. |
portal-dictionary-propose | Propose a link, a new term or a supersession. You get back an id whose status you can poll. |
discover-ecosystem-credentials | The digital credentials governed in this ecosystem, and who issues them. |
portal-quote-skill-price | What a call would cost. Free; nothing runs (section 6). |
tip-invoke-skill | Run a skill: slug, plus the skill’s own arguments. |
tip-invoke-skill-tool | Run one named tool inside a skill (skill_slug, tool_name, args). |
tip-run-list, tip-run-get, tip-property-runs | Your own run records: list, one run in full, and every run about one property. |
submit-invocation-feedback, submit-repo-feedback | Feedback (section 10). |
tip-key-introspect | See section 3. |
Categories you will meet: orchestrated_package (multi-source packages), data_access (one
wrapper per upstream source) and domain_computation (results that are computed, not fetched).
A slug runs its latest published version. To pin one, put version inside arguments.
5. Your first property calls: the products first
Four product skills come first. Each takes one address in Canada or the United States:
property-scout($0). A check before any lookup. Send an address to learn which package would answer it and at what price. Or send an answer you already hold, to learn whether it describes the unit, the building or the parcel. It calls no paid source.property-open-data($0). What open public records hold: the parcel identifier and map point, plus assessment, zoning, permits, planning layers and images where open sources publish them. Each value names its source. It never calls a paid source.property-base(priced). Property details and one valuation, from the main paid source for that place. Pass along whatproperty-open-datareturned:parcel_id,parcel_id_type,open_data_latandopen_data_lon. If no source covers the place, the call is refused at no charge.property-premium(priced, Canada). An estimated value range, a flood score and a fire insurance grade, each bought only where it answers. It bills per product delivered, and a call that delivers nothing is free. In British Columbia, pass the parcel identifier aspid.
Quote 3 and 4 before you call them, and pass maxPrice (section 6). On the lean surface, every
call goes through tip-invoke-skill:
// Toronto City Hall
{ "jsonrpc": "2.0", "id": 2, "method": "tools/call",
"params": { "name": "tip-invoke-skill",
"arguments": { "slug": "property-scout",
"arguments": { "address": "100 Queen St W, Toronto, ON" } } } }
// The same building, open records only, with the address sent as parts (section 7)
{ "jsonrpc": "2.0", "id": 3, "method": "tools/call",
"params": { "name": "tip-invoke-skill",
"arguments": { "slug": "property-open-data",
"arguments": { "line1": "100 Queen St W", "locality": "Toronto", "region": "ON",
"idempotencyKey": "<a fresh id for this call>" } } } }
The layer underneath
The products route each address to a package for its province, state, city or county, and you can
call one directly: every US state as <state>-property (for example california-property),
provinces such as ontario-property, cities such as nyc-property, toronto-property and
vancouver-property, and counties such as king-county-property. To list them all, call
tip-list-skill-family with family_id: "property".
US state packages take address (line 1 or the full one-line form), county (it
disambiguates), city (only together with county; a city sent alone is dropped), zipCode
(strongly recommended in dense cities) and unitNumberStripped (the unit without its prefix).
6. Price, retries and run records
- Read
pricingfirst. It ispriced,freeorunpriced.unpricedmeans the Data Studio has not set a price. It does not mean free, so never infer free from a missing or nullcost. - Quote with
portal-quote-skill-price. Passskills: one slug, or an array of up to 25. Nothing runs, nothing is billed, and nothing is recorded.chargeis what one call bills.ceiling: truemeans “up to this amount”, for skills billed per product delivered.reachable: falsemeans the skill cannot be invoked, so do not show that price to anyone.
maxPriceis the most you agree to be billed for one call. If the price in effect is higher, the call is refused before anything runs, at $0, withmax_price_exceeded. Set it from your quote.invocationCostis what the call billed. It rides the result’s_metatogether withinvocationIdandcorrelationId. A successful call also carries it in the body, withamount: 0when the call was free.idempotencyKey(up to 200 characters, or theIdempotency-KeyHTTP header). Mint a fresh key for each call and send the same key on its retry. If the first call delivered and billed, the retry returns that answer again at $0, withreplayed: true, for 15 minutes. If the first call is still running, the retry waits up to 60 seconds, then returnsstill_running. A key reused for a different request getsidempotency_key_reused; an argument and a header that disagree getidempotency_key_conflict.correlationIdis your own id for a run. It is echoed in_meta, andtip-run-getreads the run back by it.
On the lean surface, put maxPrice, idempotencyKey, correlationId and version inside
arguments, beside the skill’s own inputs.
7. Sending an address
The Data Studio’s rule is that a data source never receives raw typing. You get the best answers when your app does the same:
-
let an address search pick the building, with the unit in a box of its own;
-
show the person what will be looked up, and wait for a yes before you call.
-
One line or parts, never both. The products take either
address("350 5th Ave, New York, NY 10118") or the address as parts:line1(civic number and street, no unit),unit(the unit only, no prefix:1205,4B),locality(city or town),region(province or state, by code or name; it also names the country) andpostal(postal or ZIP code). With parts, the Data Studio writes the address string each source needs.localityandregionare required withline1;postalis optional but always sharpens the match. -
If you write the one-line string yourself: in Canada put the unit first (
<unit>-<number> <street>, <city>, <PROV>); keep the postal code for Québec and leave it out for BC and Ontario. In the US, send the unit on its own rather than in the street line. -
place_idandlat/lonfrom your search are accepted. The Data Studio recordsplace_idwith the run.property-open-datauseslat/lononly to choose the package and never serves them back.
8. Reading the answer
The answer is JSON in result.content[0].text. isError: true marks a failure (section 9).
- One envelope shape. Answers share one envelope, built from the ecosystem dictionary’s
sections, with PascalCase names such as
Address,Property,Valuation,TaxHistoryandBuildingPermits. Apart from the identifier type, your parser does not need to branch on place. - Absences are explained, not hidden. When a section is missing, the envelope may carry
<Section>_gap_rationaleat its root,section_gap_rationaleinside the section, or<Field>_gap_rationaleon a field. The structured form hasabsence: { code, params }: branch onabsence.code.messageis English prose for people. Older rationales are prose only, so accept both shapes. served_by_sibling_skillis not a gap. Itsparams.available_fromnames the skill slug that serves this data (for exampleproperty-open-data). Call that skill.- Provenance.
Provenance.FieldProvenancemaps each field to the source that produced it (or"derived"/"user-input").Provenance.Fetchesrecords, once per source, when it was read, an integrity hash, freshness, an evidence URL and whether the data is public. The products addProvenance.Route, which names what ran. - Verdict fields at the root, such as
canonical_check_status,match_confidenceorconfirmation_required, appear where they apply. The set is open: tolerate keys you do not know.
9. Errors
Invocation errors carry a top-level error_code from the Data Studio’s closed registry of codes. Branch on
the code, never on message. The registry is append-only: codes can be added, and existing codes
are not renamed.
error_code | Meaning | Retry? |
|---|---|---|
anchor_unresolved, no_matching_record, jurisdiction_not_covered | A calm “not found” answer. Render it. | No |
input_invalid | The arguments failed validation. | No; fix the input |
unit_required | The parcel has several units. Send unit. | No; add the unit |
skill_not_found | No such skill for your key. A skill your key cannot see answers the same way. | No |
not_entitled | You can see the skill, but your access does not cover it. | No |
max_price_exceeded | The price is above your maxPrice. Nothing ran and nothing was billed. | No; re-quote |
upstream_timeout, upstream_unavailable, upstream_throttled | An upstream source failed or is busy. | Yes, with backoff |
upstream_rejected_request, upstream_not_entitled, upstream_response_unusable | An upstream source refused the request or sent an unusable answer. | No |
skill_step_failed, skill_not_executable, internal_error | A fault on the Data Studio’s side. Report it (section 10). | No |
Transport refusals happen before any skill runs, so nothing is billed:
| Code | HTTP / JSON-RPC | When |
|---|---|---|
key_family_not_accepted | 403 / -32004 | The consumer lane refused a write-capable key |
key_call_limit_exceeded | 429 / -32005 | Too many skill calls per minute for this key |
still_running | 409 / -32006 | A retry arrived while the first call with that key was still running |
idempotency_key_reused | 422 / -32007 | The same key was sent with a different request |
idempotency_key_conflict | 400 / -32008 | The argument key and the header key disagree |
A missing, invalid or revoked key gets HTTP 401.
10. Limits and feedback
Limits.
- Failed authentication is capped at 20 attempts per minute per IP address. Past that you get
HTTP 429 (JSON-RPC -32005); wait for the time in
Retry-After. - The Data Studio can cap how many skill calls one key makes per minute. Over the cap, the call is refused at
$0 with
key_call_limit_exceededand a retry time.tools/listand the discovery tools do not count. Ask your Data Studio contact for your key’s limit. - Back off and retry. Never retry in a tight loop.
Feedback.
submit-invocation-feedbackrates one call, and the rating is recorded on that run. Passinvocation_id(from_meta.invocationId),status(acceptedorrejected), and an optionalreasonthat explains a rejection. You can only rate calls made in your own tenant.submit-repo-feedbackis for anything not tied to one call: requests, bugs, doc gaps, friction. Passtitleandbody, an optionalcategory(feature_request,bug_report,doc_issue,friction,observationorother), and optionalrelated_invocation_ids(an array). The Data Studio team triages every entry.
11. Why your catalog may differ from a colleague’s
Three gates decide what a key sees:
- Published only. Drafts and archived skills never appear.
- No system skills. Internal platform skills are never served to external keys.
- Per-key access. If your key has roles, the roles alone decide: each role’s allowed
tags, minus its denied tags, are matched against each skill’s tags. If it has none, its
tier decides, and a
consumerkey sees the skills marked consumer-visible. Tiers areconsumer(read only),author(adds write tools when a role grants them) andadmin(an older alias forauthor).
A change to your key’s roles can take up to 30 minutes to show in tools/list, because the Data Studio
caches each key’s tool list. To see what your key is right now, call tip-key-introspect.
12. Authoring skills
Author-tier keys can create and update skills over MCP, through tip-skill-create-from-bundle,
tip-skill-update, tip-skill-comment and tip-skill-get-bundle. If you want to publish your
own skills, ask your Data Studio contact for an author key, then read Authoring skills.
An author key can write, so the consumer lane refuses it: connect without lane=consumer.
Reference
- Developer Portal:
https://developer.<env>.trustinfrastructure.com/portal(production:https://developer.trustinfrastructure.com/portal). It has the API Reference tab. - OpenAPI 3.0 description:
https://api.<env>.trustinfrastructure.com/api/openapi.json. Send your key. - In the Data Studio itself:
tip-key-introspectdescribes your key;portal-quote-skill-priceprices a call before you make it;portal-browse-dictionarydefines every field name.
- Consumer reference and Authoring skills on this site.
- Your Data Studio contact, for keys, limits and anything this guide does not cover.