---
title: Write your own
description: Start a package from a template.
---

The [quickstart](/functions/quickstart) deployed a package someone else wrote. This page starts one of your own from a template. Each template holds one working tool, the build script that packs the bundle Subako takes, and tests that run the tool as ordinary code.

## Start from a template

**Rust**

You need Rust with the `wasm32-wasip1` target, and
[cargo-generate](https://github.com/cargo-generate/cargo-generate).

```bash
rustup target add wasm32-wasip1
cargo install cargo-generate
cargo generate subako-ai/subako-function-template-rust --name hello
cd hello
```

`src/main.rs` holds one tool. Its doc comment is what the model reads, and
its input type is the tool's schema:

```rust src/main.rs
use schemars::JsonSchema;
use serde::{Deserialize, Serialize};
use subako_function_sdk::{ToolError, function, package};

#[derive(Deserialize, JsonSchema)]
struct GreetInput {
    /// Who to greet.
    name: String,
}

#[derive(Serialize, JsonSchema)]
struct Greeting {
    message: String,
}

/// Greet someone by name.
#[function]
fn greet(input: GreetInput) -> Result<Greeting, ToolError> {
    Ok(Greeting { message: format!("Hello, {}!", input.name) })
}

package! {
    name: "hello",
    description: "Says hello.",
    functions: [greet],
}
```

```bash
cargo test
./package.sh        # writes dist/hello.tar.gz
```

**TypeScript**

You need Node 20 or newer.

```bash
git clone https://github.com/subako-ai/subako-function-template-typescript hello
cd hello
```

Rename `my-function` to `hello` in `package.json` and
`subako-package.json`. `src/index.ts` holds one tool, with its schemas in
zod:

```ts src/index.ts
import { serve, tool } from "@subako-ai/function-sdk";
import { z } from "zod";

const add = tool({
  name: "add",
  description: "Add two numbers.",
  input: z.object({
    a: z.number().describe("the first number"),
    b: z.number().describe("the second number"),
  }),
  output: z.object({ sum: z.number() }),
  run: ({ a, b }) => ({ sum: a + b }),
});

serve([add]);
```

```bash
npm install
npm run typecheck
npm run build                       # writes dist/
npx @subako-ai/function-sdk pack    # writes hello.tar.gz
```

**Go**

You need Go 1.25 or newer.

```bash
go run golang.org/x/tools/cmd/gonew@latest \
  github.com/subako-ai/subako-function-template-go example.com/hello
cd hello
```

`main.go` is an MCP server written with the official MCP Go SDK. Each
`mcp.AddTool` is a tool, and its input struct is the tool's schema:

```go main.go
type GreetInput struct {
    Name string `json:"name" jsonschema:"who to greet"`
}

type Greeting struct {
    Message string `json:"message"`
}

func greet(ctx context.Context, req *mcp.CallToolRequest, in GreetInput) (*mcp.CallToolResult, Greeting, error) {
    if in.Name == "" {
        return nil, Greeting{}, errors.New("name must not be empty")
    }
    return nil, Greeting{Message: "Hello, " + in.Name + "!"}, nil
}

func main() {
    server := mcp.NewServer(&mcp.Implementation{Name: "hello", Version: "0.1.0"}, nil)
    mcp.AddTool(server, &mcp.Tool{Name: "greet", Description: "Greet someone by name."}, greet)
    subakofn.Serve(server)
}
```

```bash
go test ./...
bash package.sh     # writes dist/hello.tar.gz
```

The result is a **bundle**: a `.tar.gz` holding `subako-package.json`, `module.wasm`, and any `assets/` the module reads. [Packages and versions](/functions/packages) describes it in full.

## Try it on your machine

The Rust and Go templates' tests call the tool as plain code — `cargo test` and `go test` — with no Subako involved, and `npm run typecheck` checks a TypeScript package against the globals its runtime provides. To run the built module itself the way Subako does, see [Testing locally](/functions/abi#testing-locally) in the ABI.

## Upload it, and every version after

**Console**

**Functions** › **Upload package** takes the archive, as in the
[quickstart](/functions/quickstart#upload-it). Each later build goes up
with **Upload new version** on the package's page.

**CLI**

```bash
subako function create dist/hello.tar.gz              # version 1
subako function push <function-id> dist/hello.tar.gz  # every version after
```

Either command also takes the directory to pack, in place of the archive,
so CI can push a version on every merge. See [the CLI](/build/cli#functions).

Then grant it to an agent under **Function packages**, as the quickstart does. An agent version that follows the package's **latest** version picks up each push in the sessions started after it.

## Next

**[The runtime](/functions/runtime)**

The volume, assets, HTTP, images, and the limits a call runs under.

**[Rust](/functions/rust)**

Tools, errors, files, HTTP, and images with the Rust SDK.

**[TypeScript](/functions/typescript)**

The same with the TypeScript SDK, built without a WebAssembly toolchain.

**[Examples](/functions/examples)**

Complete packages to read and start from.
