Security model
Boundaries enforced in code, each one covered by the test suite. This page is written so you can hand it to a customer's security team and have it survive being checked.
Validation before execution
Every tools/call is validated against the capability's own JSON Schema before anything is sent onward. A call that fails validation is refused at Meetext, and your backend never sees the payload.
That ordering is the whole point. The hosted MCP endpoint is addressable by a model, and a model is an untrusted argument generator by construction. Putting schema enforcement in front of the network hop means the surface your backend is exposed to is the surface you approved, not the surface a language model imagined.
- An unknown tool name is refused with a JSON-RPC invalid parameters error.
- Arguments that are not an object are refused before schema checking begins.
- A capability marked as acting on behalf of a user is refused unless the destination supplied a verified subject. It is never defaulted to a service account.
- A result larger than 1 MB is truncated rather than streamed onward.
Audited by name, never by value
Every execution is recorded. What is recorded is which capability ran, for which customer environment, at what time, with what outcome, how long it took, and which argument names were supplied.
The values are not recorded. Not truncated, not hashed, not stored encrypted for later. An audit log that holds argument values is a copy of your customers' data in a system whose whole job is to be kept for a long time and read by a lot of people.
{
"action": "capability.executed",
"capability": "search_campaigns",
"environment_id": "env-8f21...",
"arguments": ["query", "limit"],
"ok": true,
"latency_ms": 214
}Credential shaped keys are redacted from every event, audit entry and log line before they are written, so a token that ends up in an error payload does not reach storage.
Credential scoping
Credentials are never stored in the database. Rows hold a Secret Manager name, and the name embeds the customer environment id.
env--a91f4c3e-77bd-4c1a-9d02-6b2e51f0aa14--access_tokenThe scoping is structural rather than procedural. Resolving a credential requires knowing which environment you are acting for, because the environment id is part of the name being looked up. There is no query that returns another customer's secret by accident, because there is no name to ask for.
Secrets are resolved just in time at execution and are not cached across customers. Rotations, including canary rotations that validate a new credential before activating it, are recorded in the audit history.
Tenant isolation
| Boundary | Behaviour |
|---|---|
| MCP bearer token | Resolves to exactly one customer environment. A token issued for a different product is rejected without revealing whether that product exists. |
| Cross tenant reads | Return 404, not 403. A 403 confirms the resource exists. |
| OAuth state | Single use. A replayed callback cannot bind a second installation. |
| Webhooks | Signature verified and idempotent. A retried delivery never creates a second installation. |
| Generated code | Adapter code produced by Meetext is generated into a sandboxed context and reviewed as a pull request, never executed on the way in. |
| Scopes | Requested at least privilege for the approved capability set, and the granted scopes are compared against that set during validation. |
Where AI is allowed to decide
Models are used for descriptions, for suggesting mappings between a discovered operation and a destination, and for drafting adapter code that a human reviews as a pull request.
Models never decide:
- The risk level of a capability.
- Which scopes are requested.
- Whether an argument satisfies a schema.
- Whether a deployment is healthy.
- Whether a capability may be exposed at all.
All of those come from deterministic code. It is a line worth being able to state plainly, because "an AI decides what your AI can do" is the objection that ends an enterprise security review.
The shareable security review
Your customer's security team can review what a deployment accesses before anything is granted. Meetext produces a review document per customer environment, snapshot by snapshot, shared through a link.
# Open a review case for one customer environment.
curl -X POST "$MEETEXT_API/v1/environments/$ENV_ID/security-review" \
-H "authorization: Bearer $MEETEXT_API_KEY"
# Issue a shareable link for a specific snapshot.
curl -X POST "$MEETEXT_API/v1/security-reviews/$CASE_ID/shares" \
-H "authorization: Bearer $MEETEXT_API_KEY"A share is issued for one snapshot, so the document a reviewer approved is the document they saw. Shares can be revoked, and the renewals endpoint lists reviews whose snapshot no longer matches the deployment.
Attribution and confidence
Every statement in the review shows where it came from. A reviewer's real question is not what a sentence says but who is standing behind it.
| Source | Means |
|---|---|
| Observed | Meetext saw this happen: a call that returned, a scope that was granted, a capability that executed. |
| Inferred | Derived from observations, with the confidence recorded rather than dropped. |
| Vendor | You asserted it. Presented as your claim, never as Meetext's observation. |
| Your environment | Observed inside the customer's own tenant during validation. |
Confidence is an encoded value with a policy table behind it, not a label. Something Meetext could not establish stays unknown, and unknown is displayed as unknown. The product is not permitted to round it up.