Using the App¶
This document describes the common use cases for this app.
General Usage¶
The app holds three groups under one AI Tools menu.
- AI Tools → AI Models lists AI Providers and AI Models: the LLM endpoints and the models that each one offers.
- AI Tools → MCP Models lists MCP Servers and MCP Tools: the MCP servers and what each one advertises.
- AI Tools → Agents lists AI Agents, AI Tools, AI Skills, and Agent Threads: what an agent is made of, what it may reach, and the conversations it left behind.
Nothing here calls a model. The first two groups record what exists. The third describes an agent, so that another app builds one from rows instead of from Python.
AI providers and models¶
How to add a provider¶
- Go to AI Tools → AI Models → AI Providers. Select Add.
- Enter a Name.
- Select an External Integration. If the integration does not exist, select the + button beside the field. A modal opens. Create the External Integration there, and the app selects the new record for you. You do not leave the provider form.
- Select a Provider type. This is the API dialect that a consuming app uses to address the endpoint.
- Keep OpenAI-compatible set if the endpoint gives
GET /v1/models. This is a different question from the provider type. Ollama answers yes to both. - Set a default num_predict and temperature, if you want them.
CAUTION: An openai_compatible or an ollama provider is an address, not a service. Give one an
External Integration with a remote URL. Without a URL, a client goes to another company's endpoint.
The + button beside the field opens this modal. Complete it and select Create. The app then selects the new External Integration on the provider form behind the modal.
How to discover the models that a provider offers¶
Run Jobs → AI Models → Discover AI Models.
The job reads GET <remote_url>/v1/models from each OpenAI-compatible provider and syncs the
result. You can run it as often as you want:
- The job creates a record for a model in the response that is not in the database.
- A model that is already in the database keeps each column that an operator owns:
enabled,kind,num_predict,temperature, both costs, anddefault_parameters. A user can set any of them by hand, so the job never overwrites them. - The job logs and keeps a model in the database that the provider no longer offers. It never deletes a record.
Leave AI Provider empty to run against each enabled provider. Clear Enable new models to create new records in the disabled state, for a review.
The job skips a provider that is not OpenAI-compatible, and says so. No standard discovery endpoint exists for such a provider.
The job also skips a disabled provider. This applies both when you name that provider directly and when the job runs against every provider. A provider that you take out of service does not come back on the next discovery run.
The job creates each new model with Kind set to chat. GET /v1/models returns chat models
and embedding models together and says nothing about which is which. Correct the embedding models
by hand after the first run.
The provider detail view lists what the job found.
How to override an inference parameter for one model¶
num_predict and temperature are on both models. The provider value is the default. The model
value is an override. Leave the model value empty to inherit the default.
Read the effective value from the ORM:
How to send a parameter that has no field of its own¶
Put it in Default parameters on the model, as a JSON object. Use seed for a run that must
repeat. Use reasoning_effort for a reasoning model. Use top_k and top_p for a local model.
Use extra_body for anything that a unified client has no name for.
The app accepts only the keys on its allowlist. No key on that list decides which host answers.
Read the whole set through ai_model.resolved_parameters, which applies the allowlist again and
adds the resolved temperature. See AI Model.
How to record what a model is for¶
Set Kind to chat or to embedding. The two are not interchangeable and they are not the same
endpoint. This lets a consuming app refuse a wrong configuration before it sends anything.
How to retire a model without a delete¶
Clear the Enabled checkbox. The record stays, its history stays, and the discovery job does not change the flag. A consumer must skip a disabled model.
How to take a whole provider out of service¶
Clear the Enabled checkbox on the provider. Discovery skips it, and an app that reads this
registry must skip each model on it. Ask ai_model.is_available in place of a check on both flags.
CAUTION: The discovery job creates a model with Enabled set. A provider that you retire one model at a time comes back on the next run. Do not disable each model.
CAUTION: A delete of the provider also deletes each model record on it, together with the cost data. Do not delete a provider that you want to keep.
How to record what a model costs¶
Set Input cost per million tokens and Output cost per million tokens on the model. A consumer can then price a call before it makes one, or account for one afterward. Output usually costs several times more than input. That is why the two are separate fields.
CAUTION: An empty price means that nobody recorded one. Treat it as unknown, not as free.
How to examine every model at once¶
The AI Models list shows each model of each provider. Filter it by provider, by kind, by enabled state, or by name.
MCP servers and tools¶
How to register a server¶
- Go to AI Tools → MCP Models → MCP Servers. Select Add.
-
Give the server a name and select its External Integration.
If the integration does not exist, select the + button beside the field. The External Integration form opens in a modal over the page. Save it, and the app selects the new integration without a loss of what you typed. This needs the
extras.add_externalintegrationpermission. Without that permission, Nautobot hides the button. -
Select the Transport. Almost every remote server is
streamable-http, and that is the only transport that discovery reads. Astdioserver runs as a subprocess of its client, so a worker cannot reach one. The MCP specification deprecatessse, and this app does not speak it. Discovery skips both and says so. Enter their tools by hand. - Save.
How to discover what a server offers¶
Open the server and select Run Discovery. You can also run Jobs → MCP Models → MCP Server Discovery directly. Leave the server empty to discover each enabled server. That is the form to schedule.
Discovery records what the server said. It never enables a tool and it never sets writable.
A discovered server shows five things: what the operator set, what the server reported about itself, its advertised capabilities, its own instructions, and each tool that it offers.
How to review the tools¶
A newly discovered tool arrives enabled, with writable set to True. Assume that the tool writes
until a person has read what it does.
Two optional settings change this. Set new_tools_enabled to False, and a new tool arrives
switched off, so nothing is on offer before a person reads it. Set disable_on_definition_change
to True, and the app switches a tool off when its definition moves after a review. See
Install.
Go to AI Tools → MCP Models → MCP Tools. Read the description and the input schema of each new
tool. Select the tools that only read. Use Edit Selected to clear writable on all of them at
once.
The Advertised Read Only column shows the claim of the server.
WARNING: The MCP specification tells a client not to decide from an annotation of a server. Treat this column as a hint from a party that nothing verified. Use it only to compare the claim against the description.
Open a tool to see both advertised JSON Schemas, and the fingerprint that says whether the contract moved since the review:
How to register a server that Nautobot cannot reach¶
A stdio server has no endpoint for a worker to open. Register the server, then add its tools by
hand from AI Tools → MCP Models → MCP Tools → Add. Each other part of the record works normally.
How to read a registry from another app¶
This is what the app is for. Both registries are plain Nautobot models, so another app reads them through the ORM or the REST API.
The AI registry¶
Filter on both flags. A model on a disabled provider is not on offer, whatever the flag on the
model says. ai_model.is_available asks the same question about one record.
from nautobot_ai_models.models import AIModel
available = AIModel.objects.filter(
enabled=True,
provider__enabled=True,
).select_related("provider__external_integration")
Read provider_type to find out how to address each endpoint.
WARNING: The OpenAI-compatibility layer of Ollama does not return tool calls. A client that reads
openai_compatible alone loses tool calling silently. Read provider_type.
Split the chat models from the embedding models, which are not interchangeable:
Read the effective inference parameters, with the provider default added:
for ai_model in available:
print(ai_model.name, ai_model.resolved_num_predict, ai_model.resolved_temperature)
print(ai_model.resolved_parameters)
resolved_parameters checks the allowlist again as it reads, so it is safe to send.
Read the price of a million tokens. None means that nobody recorded a price. It does not mean
free.
The MCP registry¶
from nautobot_ai_models.models import MCPTool
available = MCPTool.objects.filter(
enabled=True,
mcp_server__enabled=True,
).select_related("mcp_server__external_integration")
read_only = available.filter(writable=False)
Build the connection from the integration of the server. Render the templated fields. Do not read them raw, because all three support Jinja2:
integration = tool.mcp_server.external_integration
url = integration.render_remote_url({"obj": tool.mcp_server})
headers = integration.render_headers({"obj": tool.mcp_server})
How to find that the contract of a tool changed¶
definition_fingerprint is a digest of the title, the description, and both schemas of the tool.
Record it beside the approval that you gave. A difference from the current value means that the
server changed the tool after a person reviewed it. The review is then out of date.























