Package specification
A guide to packages on Instant.bot
Last updated
Was this helpful?
A guide to packages on Instant.bot
Last updated
Was this helpful?
All packages on Instant.bot are available as both REST API servers and MCP servers. They are hosted on {package}.instant.bot
and expose HTTP endpoints that act as tools. They are built using the , a JavaScript framework and gateway that turns JavaScript functions into type-validated HTTP endpoints with built-in bindings for the MCP Streamable HTTP specification.
REST means Representational State Transfer and is a fancy way of saying these servers expose endpoints that support multiple HTTP verbs: GET
, POST
, PUT
, DELETE
.
MCP stands for Model Context Protocol and is an emerging standard for connecting LLMs to remote procedure calls. It is currently supported by OpenAI, Anthropic and Google Gemini APIs.
For an example of what hosted tools look like in production, visit:
(includes required keys)
(outputs image)
is a combined framework and HTTP gateway for turning JavaScript functions into typed HTTP endpoints. It uses JSDoc comments above function exports to automatically generate OpenAPI and JSON schemas for each endpoint, as well as tool definitions. It is vendor-agnostic, meaning Instant API projects can be hosted anywhere like Vercel, AWS, GCP, etc.
Instant API is part of a . That project itself is a derivative of , an now-defunct API framework we first built in 2015. Instant.dev is our "Ruby on Rails for JavaScript", with over 10 years of development time behind it. We have used it to build dozens of products and the most successful have handled over 1B API requests per week. In an era of LLM extensibility, we are really excited about its potential to generate tools for LLMs quickly and easily.
The Instant.bot package registry automatically hosts and scales your Instant API projects for you as packages. However, Instant API projects can be hosted on any infrastructure provider that supports Node.js 20.x or above: like Vercel, Railway and AWS. This means projects that you build on Instant.bot are transportable. When you build tools for Instant.bot you are not locked in to using us as a hosting provider, though we do manage secret storage, package verification and more.
All packages for Instant.bot are hosted on our gateway at {package}.instant.host
You authenticate into packages using API keychains, a primitive we have created for securely storing, managing and delegating access to third-party secrets and auth.
These keychains (1) authenticate you as an Instant.bot user and (2) can scaffold one or more API secrets that can be shared with packages. For security considerations, please read the API keychain specification.
Every package contains a directory structure with the following format:
As you can see, Instant.bot packages use file-based routing. Everything exported from the functions/
folder is available as an API endpoint.
This is your Instant.bot package configuration. It typically has the following format;
Where "name"
is the package name, usually of the format @user/package
for public packages. Agent code packages, which are private, are of the format agent/@user/package
.
The default timeout for Instant.bot packages is 10 seconds. This means tools that are part of this package will run for 10 seconds before automatically halting execution. You can manually change this value to any number between 1 (1 second) and 300 (5 minutes).
Every file in the functions/
directory is used to create an HTTP API endpoint that can respond to GET
, POST
, PUT
and DELETE
requests. Each of these endpoints is available to your agents for use as a tool.
Here's an example of a weather endpoint that is accessible via HTTP GET.
A single file can output up to four different endpoints corresponding to an HTTP method, and each can be used as an individual tool. GET
, POST
, PUT
and DELETE
are all supported.
For simple tools, exporting a default
function will always work as an endpoint that responds to all HTTP methods.
You can GET
, POST
, PUT
or DELETE
to this endpoint. If you'd like to know which method was called, you can use the magic context
object to check the HTTP method:
If you want different functionality depending on the HTTP method, it's best to export named functions for GET
, POST
, PUT
and DELETE
.
If you don't export a specific HTTP method, requests to that method will fail with a 501: Not implemented
error.
To create arguments for your endpoint, you simply add arguments to your function signature and comment them using JSDoc.
When you make an HTTP POST request to this endpoint, you can provide an application/json
payload with {"name":"test","age":20}
and will receive the result "Hello test, you are 20!"
.
To set arguments as required, simply do not provide a default value for the argument in the function signature.
If you try to send an HTTP POST with an empty payload, you will receive the following response:
To mark an argument as optional, simply give it a default value. A default value must be either null
or match the type specified by JSDoc.
Sending an empty payload will now result in a "Hello world!"
response.
Every function signature can include an optional context
argument. The context argument must always be the last argument when provided and must not be documented by JSDoc.
The context
object contains contextual runtime data, like the HTTP method, raw HTTP body, parameters and more.
context.name
the function name
context.path
an array of path segments used to run the endpoint
context.params
a SON representation of arguments passed in to the endpoint
context.remoteAddress
the IPv4 or IPv6 address that requested the endpoint
context.uuid
a unique execution id
context.http.method
the HTTP method used to invoke the endpoint
context.http.headers
the HTTP headers used to invoke the endpoint
context.http.body
a raw utf-8 string of the HTTP POST body, if applicable
context.keychain.key("MY_KEY")
a method to access API keychain keys provided to the endpoint at runtime
Similar to arguments, you can specify return types for endpoints. By default, the return type is any
. We will allow any return type, though there are a few special cases.
You can specify endpoint returns
types with the @returns
directive:
By specifying the return type, you can force our gateway to do type validation on responses as well: if something goes wrong, we can throw an error. For example, in this code the return type is specified as number, but it's returning a string.
We'll get the following error.
To have a package return attachments in the Instant.bot UI, simply make sure you set the return type to buffer
like so:
By setting the @returns
directive you tell the OpenAPI spec and tool schema that we expect to return a file. The image/png
content type header gets read from the .contentType
field of the buffer. This content type will direct the attachment within the UI to render an image.
To throw custom errors from an endpoint you have two options.
boolean
True or False
true
or false
string
Basic text or character strings
"hello"
, "GOODBYE!"
number
2e+100
, 1.02
, -5
float
Alias for number
2e+100
, 1.02
, -5
integer
Subset of number
, integers between -2^53 + 1
and +2^53 - 1
(inclusive)
0
, -5
, 2000
object
Any JSON-serializable Object
{}
, {"a":true}
, {"hello":["world"]}
object.http
An object representing an HTTP Response. Accepts headers
, body
and statusCode
keys
{"body": "Hello World"}
, {"statusCode": 404, "body": "not found"}
, {"headers": {"Content-Type": "image/png"}, "body": Buffer.from(...)}
array
Any JSON-serializable Array
[]
, [1, 2, 3]
, [{"a":true}, null, 5]
buffer
Raw binary octet (byte) data representing a file.
{"_bytes": [8, 255]}
or {"_base64": "d2h5IGRpZCB5b3UgcGFyc2UgdGhpcz8/"}
any
Any value mentioned above
5
, "hello"
, []
The buffer
type will automatically be converted to a Buffer
from any object
with a single key-value pair matching the footprints {"_bytes": []}
or {"_base64": ""}
.
Otherwise, parameters provided to a function are expected to match their defined types. Requests made over HTTP GET via query parameters or POST data with type application/x-www-form-urlencoded
will be automatically converted from strings to their respective expected types, when possible.
Once converted, all types will undergo a final type validation. For example, passing a JSON array
like ["one", "two"]
as a string to a parameter that expects an object
will convert from string to JSON successfully but fail the object
type check, as it is an array.
boolean
"t"
and "true"
become true
, "f"
and "false"
become false
, otherwise will be kept as string
string
No conversion: already a string
number
Determine float value, if NaN keep as string, otherwise convert
float
Determine float value, if NaN keep as string, otherwise convert
integer
Determine float value, if NaN keep as string, otherwise convert: may fail integer check
object
Parse as JSON, if invalid keep as string, otherwise convert: may fail object check
object.http
Parse as JSON, if invalid keep as string, otherwise convert: may fail object.http check
array
Parse as JSON, if invalid keep as string, otherwise convert: may fail array check
buffer
Parse as JSON, if invalid keep as string, otherwise convert: may fail buffer check
any
No conversion: keep as string
You can combine types using the pipe |
operator. For example;
Will accept a string
or an integer
. Types defined this way will validate against the provided types in order of appearance. In this case, since it is a GET request and all parameters are passed in as strings via query parameters, myparam
will always be received a string because it will successfully pass the string type coercion and validation first.
However, if you use a POST request:
Then you can pass in {"myparam": "1"}
or {"myparam": 1}
via the body which would both pass type validation.
You can combine as many types as you'd like:
Including any
in your list will, as expected, override any other type specifications.
Similar to combining types, you can also include specific JSON values in your type definitions:
This allows you to restrict possible inputs to a list of allowed values. In the case above, sending ?myparam=4
via HTTP GET will successfully parse to 4
(Number
), because it will fail validation against the three string options.
You can combine specific values and types in your definitions freely:
Just note that certain combinations will invalidate other list items. Like {1|2|integer}
will accept any valid integer.
The types string
, array
and buffer
support sizes (lengths) via the {a..b}
modifier on the type. For example;
Would expect alpha
to have a maximum length of 9
, beta
to have a minimum length of 2
but a maximum length of 6
, and gamma
to have a minimum length of 5
.
The types number
, float
and integer
support ranges via the {a,b}
modifier on the type. For example;
Would expect alpha
to have a maximum value of 1 200 000 000
, beta
to have a minimum value of -10
but a maximum value of 10
, and gamma
to have a minimum value of 0.87
.
Arrays are supported via the array
type. You can optionally specify a schema for the array which applies to every element in the array. There are two formats for specifying array schemas, you can pick which works best for you:
For multi-dimensional arrays, you can use nesting:
Please note: Combining types are not currently available in array schemas. Open up an issue and let us know if you'd like them and what your use case is! In the meantime;
Would successfully define an array of integers or an array of strings.
To define object schemas, use the subsequent lines of the schema after your initial object definition to define individual properties. For example, the object {"a": 1, "b": "two", "c": {"d": true, "e": []}
Could be defined like so:
To define object schemas that are members of arrays, you must identify the array component in the property name with []
. For example:
The process for parameter validation takes the following steps:
Read parameters from the HTTP query string as type application/x-www-form-urlencoded
If applicable, read parameters from the HTTP body based on the request Content-Type
Supported content types:
application/json
application/x-www-formurlencoded
multipart/form-data
application/xml
, application/atom+xml
, text/xml
Query parameters can not conflict with body parameters, throw an error if they do
Perform type coercion on application/x-www-form-urlencoded
inputs (query and body, if applicable)
Validate parameters against their expected types, throw an error if they do not match
During this process, you can encounter a ParameterParseError
or a ParameterError
both with status code 400
. ParameterParseError
means your parameters could not be parsed based on the expected or provided content type, and ParameterError
is a validation error against the schema for your endpoint.
application/x-www-form-urlencoded
Many different standards have been implemented and adopted over the years for HTTP query parameters and how they can be used to specify objects and arrays. To make things easy, Instant API supports all common query parameter parsing formats.
Here are some query parameter examples of parsing form-urlencoded data:
Arrays
Duplicates: ?arr=1&arr=2
becomes [1, 2]
Array syntax: ?arr[]=1&arr[]=2
becomes [1, 2]
Index syntax: ?arr[0]=1&arr[2]=3
becomes [1, null, 3]
JSON syntax: ?arr=[1,2]
becomes [1, 2]
Objects
Bracket syntax: ?obj[a]=1&obj[b]=2
becomes {"a": 1, "b": 2}
Dot syntax: ?obj.a=1&obj.b=2
becomes {"a": 1, "b": 2}
Nesting: ?obj.a.b.c.d=t
becomes {"a": {"b": {"c": {"d": true}}}}
JSON syntax: ?obj={"a":1,"b":2}
becomes {"a": 1, "b": 2}
With Instant API, query and body parameters can be used interchangeably. If you send both query parameters and body parameters to an endpoint, they will be combined into a single parameter object. GET and DELETE requests only support query parameters.
Public packages can request secret keys from API keychains. For example, the requires a STRIPE_SECRET_KEY to use it successfully. To set these per package, add them to your instant.package.json
like so:
You get tool type validation for free when building packages with Instant.bot as part of the framework. You can define types for both @param
and @returns
arguments in the function signature. Instant API supports the following types.
Any double-precision value
That covers the basics of building packages on Instant.bot. If you have any questions, please do not hesitate to jump into our community Discord server at .