Skip to content

bug: docs show context.input in middleware/interceptors, but tool arguments never reach the ExecutionContext #328

Description

@vnmchat13

Summary

The docs show context.input being read inside middleware, and an interceptor example keyed on context.metadata?.input — but ExecutionContext has no input field and the framework never populates one. Tool arguments are reachable only from Pipes and the tool handler, never from a guard, middleware, or interceptor.

The practical effect is that there is no argument-aware around extension point — which is the classic cross-cutting use case (auditing, authorization, caching keyed on inputs).

Environment

  • NitroStack package(s) and version(s): @nitrostack/core@1.0.14 (also verified against main), @nitrostack/cli@1.0.15, @nitrostack/widgets@1.0.8
  • Node.js version (node -v): v24.18.0
  • npm version (npm -v): 11.16.0
  • OS and version: macOS 26.2

Steps To Reproduce

  1. Create an interceptor (or middleware) that tries to read the tool's arguments from ExecutionContext.
  2. Bind it to a tool with @UseInterceptors(...) / @UseMiddleware(...).
  3. Call the tool with any arguments.

Expected Behavior

Per the SDK reference, context.input contains the tool's arguments, so a middleware/interceptor can inspect what it is wrapping.

Actual Behavior

context.input is undefined. ExecutionContext contains only requestId, toolName, logger, metadata, auth, and task. context.metadata is the MCP _meta field, so it is {} unless a client happens to send _meta.

Minimal Reproduction

@Interceptor()
export class ArgsInterceptor implements InterceptorInterface {
  async intercept(context: ExecutionContext, next: () => Promise<unknown>): Promise<unknown> {
    console.log('input:',    (context as any).input);  // undefined
    console.log('metadata:', context.metadata);        // {} unless the client sent _meta
    return next();
  }
}

Where the documentation says otherwise

Three separate surfaces describe this capability:

  1. SDK reference (middleware)context.logger.info('Tool starting', { tool: context.toolName, input: context.input })
  2. Interceptors guide — a CacheInterceptor example keying its cache on context.metadata?.input
  3. Official agent skill middleware-pipeline"Interceptors can transform/intercept input arguments or mapped output from a tool method execution." (the interface shown directly beneath it is intercept(context, next))

The context.input example has also been copied into the sample apps, so the pattern is propagating. Worth noting the skills are consumed by AI coding assistants, which will confidently generate interceptors that read context.input.

Additional Context

Why the framework cannot provide it today (all in the published package, and unchanged on main):

  • server.jscreateContext() returns { logger, requestId, toolName, metadata }
  • server.jstool.execute(args, context): arguments are passed as a separate parameter
  • tool.js — pipeline is Guards → Middleware → Interceptors → Pipes → Handler; only executeWithPipes() and the handler ever receive the input
  • server.jsconst { _meta, ...toolArgs } = args; createContext({ metadata: _meta }), so metadata.input only exists if a client sends it

Impact for us. We are building an approval-gate interceptor: it pauses a sensitive tool call, asks a human to confirm out-of-band, and only calls next() once approved. Without access to the arguments we cannot:

  • describe the action to the human (our confirmation card reads "Book flight" rather than "SFO→JFK · $168.45"), or
  • derive a per-action idempotency key — so a repeat call is indistinguishable from a retry of the previous one, which is a correctness problem for approval/authorization interceptors.

Pipes do receive the arguments, but they get no ExecutionContext (no auth, no task, no requestId) and cannot wrap the handler, so they can't serve as around-advice.

Suggested fix: attach input to the ExecutionContext handed to the pipeline, or thread it into the use() / intercept() signatures. Either would satisfy the already-documented behavior. Happy to open a PR if you have a preferred shape.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions