Noodle Seed
Guides

Tools, resources & prompts

Author the core MCP surface with the @noodleseed/one SDK. Typed tools, readable resources, and reusable prompts.

A Noodle Seed server is a server(name, options, definitions) call. The definitions array holds your tools, resources, and prompts. Everything imports from the bare @noodleseed/one package.

src/server.ts
import { server, tool, resource, prompt, z } from '@noodleseed/one';

export default server('support', { title: 'Support', version: '1.0.0' }, [
  // tools, resources, prompts go here
]);

Tools

A tool is a typed action the model can call. It declares a Zod input schema, an optional output schema, and a fulfil function that returns the result.

tool('greet', {
  description: 'Greet a person by name.',
  input: z.object({ name: z.string().default('world') }),
  output: z.object({ message: z.string() }),
  fulfil: ({ input }) => {
    return { message: `Hello, ${input.name}!` };
  },
});

tool(name, options) accepts:

OptionTypeNotes
descriptionstringShown to the model in tools/list.
inputZod schemaCompiled to JSON Schema 2020-12.
outputZod schemaOptional but recommended for structured results.
fulfil(ctx) => resultReturns the output. Receives { input, user, connectors }.
annotationsobjectBehavior hints (see below).
visibility('model' | 'app')[]Defaults to both. Use ['app'] for app-only tools.
viewReact entry or raw HTMLLinks the tool result to an MCP App view.

Annotations

Use the annotations helper to declare behavior hints so hosts can present the tool correctly.

import { annotations } from '@noodleseed/one';

tool('list_orders', {
  description: 'List recent orders.',
  input: z.object({ limit: z.number().int().min(1).max(50).default(10) }),
  output: z.object({ orders: z.array(z.object({ id: z.string() })) }),
  annotations: annotations.readOnly(),
  fulfil: ({ input }) => ({ orders: [] }),
});

annotations.readOnly(), annotations.action(), annotations.localAction(), and annotations.openAction() map to the MCP readOnlyHint / destructiveHint / idempotentHint / openWorldHint fields.

Calling connectors

To reach an external API, bind a connector on the server's use map and call it inside fulfil via ctx.connectors. See the Connectors guide for the full pattern.

tool('current_weather', {
  description: 'Get the current temperature for a city.',
  input: z.object({ city: z.string() }),
  output: z.object({ temp_c: z.number() }),
  fulfil: ({ input, connectors }) => connectors.weather.current({ city: input.city }),
});

Resources

A resource exposes readable context at a URI. The URI can be fixed or a {var} template.

resource('changelog', {
  uri: 'docs://changelog',
  title: 'Changelog',
  mimeType: 'text/markdown',
  fulfil: () => ({
    contents: [{ uri: 'docs://changelog', mimeType: 'text/markdown', text: '# Changelog\n...' }],
  }),
});

For a templated URI, the variable is available in the resource context:

resource('case', {
  uri: 'case://{case_id}',
  title: 'Support case',
  fulfil: ({ input }) => ({
    contents: [{ uri: `case://${input.case_id}`, mimeType: 'application/json', text: '{}' }],
  }),
});

Prompts

A prompt is a reusable, argument-driven template surfaced through prompts/list. Declare arguments with a Zod object, and return messages from fulfil.

prompt('incident_update', {
  title: 'Incident update',
  description: 'Draft a customer-safe update from an internal case.',
  arguments: z.object({
    case_id: z.string(),
    audience: z.enum(['customer', 'executive']).default('customer'),
  }),
  fulfil: () => ({
    messages: [{ role: 'user', content: { type: 'text', text: 'Draft a customer-safe update...' } }],
  }),
});

Check your work

noodle validate   # compile + schema + connector-reference checks
noodle test       # loopback MCP smoke (initialize, tools/list, a tool call)

noodle validate and noodle test are local and need no account. See the Quickstart for the full loop.

On this page