Sources

A source is where capabilities come from. Three types are supported, they normalize into one internal format, and each of them tells you what it would not import.

One shape, three inputs

An MCP tool, an OpenAPI operation and a repository route are three different things on the wire. Meetext normalizes all of them into a single capability specification: a name, a description, a JSON Schema for arguments, a risk level, and a binding that records how to execute it.

That normalization is why the destinations behave identically. A Slack installation does not know or care whether the capability behind a tool call came from an MCP server or a Flask route, and neither does the hosted MCP endpoint.

Source typeMeetext readsExecution goes to
mcptools/list on your Streamable HTTP MCP serverA tools/call back to that same server
openapiAn OpenAPI document, by URL or inlineAn HTTP request built from the operation's method, path and parameters
repositoryA checked out repository, scanned per frameworkAn HTTP request against the route the candidate came from

MCP servers

The most direct source. Meetext connects as an MCP client, performs initialize, calls tools/list, and turns each tool into a capability. Tool names, descriptions and input schemas are taken as given.

Request body
{
  "type": "mcp",
  "mcp": {
    "url": "https://acme.example.com/mcp",
    "auth_header": "authorization",
    "auth_scheme": "Bearer",
    "auth_token": "..."
  },
  "discover_now": true
}

Both a JSON response and a text/event-stream response to the POST are accepted, because Streamable HTTP servers may answer with either.

Refused: a tool with no name, and a tool whose input schema is not a usable JSON Schema object. Both are reported against the tool rather than dropped.

OpenAPI documents

Give Meetext a URL or paste the document inline. Every path and method pair becomes a capability candidate. Parameters from the path item and from the operation are merged, request body schemas are folded into the argument schema, and local $ref pointers are inlined.

Request body
{
  "type": "openapi",
  "openapi": {
    "url": "https://api.acme.example.com/openapi.json",
    "base_url": "https://api.acme.example.com",
    "auth_header": "authorization",
    "auth_scheme": "Bearer",
    "auth_token": "..."
  }
}

Refused, with the reason recorded per operation:

  • Remote references. A $ref pointing at another host is rejected rather than fetched. Resolving it would mean a document you control can make Meetext issue requests to a host you did not name.
  • Deprecated operations. An operation marked deprecated: true is skipped. Shipping a deprecated endpoint to enterprise customers is a support burden with a known expiry date.
  • Operations beyond the limit. Import stops at 800 operations, and every operation past that is listed as not imported. A document that large is usually the wrong unit to expose whole.
  • Operations whose schema cannot be built. A malformed parameter or an unresolvable body schema fails that one operation and no others.

Repositories

For products that have neither an MCP server nor a published OpenAPI document. Meetext detects which frameworks the repository uses and scans for routes and service functions.

FrameworkWhat is scanned
FastAPIRoute decorators, path and query parameters, request models, plus service layer functions
FlaskRoute decorators and their methods
DjangoURL configuration and the views it resolves to
Next.jsRoute handlers under the app and pages API directories
ExpressRouter and app method registrations

A scan produces candidates, not capabilities. You choose which candidates to promote, and only then are they turned into capabilities with schemas. A repository is the least precise source, so it is the one where the human step is not optional.

Refused: a repository with no supported framework detected, which is reported as one issue naming the five frameworks that are supported, and any file that cannot be parsed, reported against its path.

What gets refused

Every source type produces the same pair: a list of specifications and a list of issues. An issue carries the identifier of the thing refused and the reason. Both appear in the discovery response and in the dashboard next to the source.

Discovery response
{
  "discovery": {
    "status": "completed",
    "imported": 24,
    "issues": [
      {
        "identifier": "DELETE /v1/accounts/{id}",
        "reason": "operation is deprecated; skipped"
      },
      {
        "identifier": "POST /v1/exports",
        "reason": "remote $ref is not resolved"
      }
    ]
  }
}

Name collisions are the one case that is resolved rather than refused. Two operations that normalize to the same capability name are disambiguated deterministically, so the same document always produces the same names.

Fingerprints and rediscovery

Every discovery result carries a fingerprint: a hash over every capability name and schema in the surface. It is stored on the source.

Rediscovery recomputes it. When the fingerprint moves, Meetext knows the source changed shape since you last approved it, and it can name which capabilities changed rather than telling you something somewhere is different. Approval for a changed capability is revoked. See Capabilities for what happens next.

Shell
curl -X POST \
  "$MEETEXT_API/v1/products/$PRODUCT_ID/sources/$SOURCE_ID/rediscover" \
  -H "authorization: Bearer $MEETEXT_API_KEY"

Customers already installed are unaffected until you publish. They are serving a frozen package.

Source credentials

The token you give a source is the one Meetext uses to call your backend. It is written to Secret Manager on connect; the source row keeps the secret name and the header and scheme to send it with. It is never returned by the API and never written to a log or an audit entry.

This credential is entirely separate from the per customer credentials obtained when an enterprise authorizes the app in their own workspace. Those are scoped to one customer environment and are covered in the security model.