Skip to main content
Glama

collection-mcp-check

Turn a Postman collection into an MCP server, then check whether an AI agent can actually use it.

cmcp lint   collection.json          # agent-readiness findings, CI exit code
cmcp tools  collection.json          # the MCP tools/list payload it would expose
cmcp serve  collection.json          # run it as an MCP server on stdio
cmcp check  collection.json --call-readonly   # spawn the server, speak MCP to it, call the safe tools

Python 3.10+, standard library only. No runtime dependencies.

Why this exists

Postman already turns collections into MCP servers (the MCP Generator on the API Network gives you one tool per request). The gap is the step after that: a collection written for humans clicking "Send" makes a shaky tool list for a model.

I ran it on echo-v2.postman_collection.json from postmanlabs/newman (the Postman Echo collection, 22 requests). What it found:

  • Set Cookies and Delete Cookies are GETs. A generator that marks every GET readOnlyHint: true tells the client these are safe to call without asking. cmcp reads the name and path, flags it (CM040), and sets readOnlyHint: false.

  • DigestAuth Success needs {{echo_digest_realm}} and {{echo_digest_nonce}}, which a test script in a different request writes with postman.setGlobalVariable. In Postman you run the folder in order. An agent calls one tool. CM023 catches that chain.

  • Digest and OAuth 1.0 auth aren't replayed by the generated server (CM060), so those calls go out unsigned.

  • 8 GET requests carry a formdata body (including a client_secret field) that the tool silently drops (CM022, CM030).

Then cmcp check --call-readonly called the 15 read-only tools against the live postman-echo.com. 12 returned 200. Of the 3 failures, 2 are exactly what lint predicted (missing chained variables, unsupported OAuth 1.0). The third is the first half of the digest flow, which gets a 401 on purpose and stores the nonce from it.

That's the point of shipping lint and live check together: the lint tells you which calls will fail before an agent finds out the hard way.

Related MCP server: Postman MCP Generator

Quickstart

git clone https://github.com/DeveshB-1/collection-mcp-check && cd collection-mcp-check
uv run cmcp lint examples/postman-echo-v2.postman_collection.json
uv run cmcp check examples/postman-echo-v2.postman_collection.json --call-readonly

# your own collection + environment export
uv run cmcp lint my.postman_collection.json --env my.postman_environment.json --strict

Use it from Claude Desktop (or any MCP client):

{
  "mcpServers": {
    "orders-api": {
      "command": "uv",
      "args": ["run", "--directory", "/path/to/collection-mcp-check",
               "cmcp", "serve", "/path/to/orders.postman_collection.json",
               "--env", "/path/to/prod.postman_environment.json"]
    }
  }
}

Variables resolve in this order: collection variables, then the --env file (a Postman environment export or a flat JSON object), then CMCP_VAR_<name> process env vars. Secrets stay out of the collection file.

What the lint checks

Rule

Severity

Catches

CM001

warn

two requests slug to the same tool name (renamed _2, and agents mix them up)

CM002

warn

name over 64 chars, truncated

CM003

error

name fails ^[a-zA-Z0-9_-]{1,64}$ (the rule the Anthropic and OpenAI tool APIs enforce)

CM010

warn

request has no description, so the model only sees GET {{baseUrl}}/...

CM011

info

description over 1,500 chars (sent on every tools/list)

CM012

warn

path or query parameter with no description

CM020

error

{{variable}} defined nowhere (collection or env)

CM021

error

body declared JSON that doesn't parse

CM022

warn

GET/HEAD with a body (dropped from the tool)

CM023

warn

depends on a variable another request's script sets

CM030

warn

literal credential in a header, query, form field or auth block

CM040

warn

GET whose name/path reads as a write; readOnlyHint set to false

CM050

error

request with no URL

CM060

warn

auth type the server won't replay (digest, oauth1, hawk, ...)

agent-readiness = share of tools with zero errors or warnings. lint exits 1 on errors (--strict: on warnings too), so it drops into CI next to a Newman run.

What the server does

  • One tool per request. Name from folder_request in snake_case, deduped, kept to 64 chars.

  • Input schema: path variables (required, with saved examples), query params (defaults from the collection), JSON body as an object with the saved example as default, form fields as an object.

  • Annotations: readOnlyHint for GET/HEAD/OPTIONS that don't read as writes, destructiveHint for DELETE/PUT/PATCH and cancel/remove/reset-style names, openWorldHint always.

  • Auth inherited collection → folder → request, as Postman does. Basic, bearer and API-key (header) are replayed.

  • Postman dynamic variables {{$guid}}, {{$timestamp}}, {{$isoTimestamp}}, {{$randomInt}}, {{$randomUUID}} are filled at call time.

  • HTTP 4xx/5xx come back as isError: true with the status, latency and the first 4,000 chars of the body.

  • MCP over stdio, protocol versions 2024-11-05 through 2025-06-18: initialize, ping, tools/list, tools/call.

It sets its own User-Agent, because postman-echo.com returns 403 to urllib's default Python-urllib/3.x (that's how I found out).

Sample run

Live run against postman-echo.com, 2026-10-25 (full output in demo/):

$ cmcp lint examples/postman-echo-v2.postman_collection.json
collection: Postman Echo (V2)
tools: 22  clean: 9  agent-readiness: 41%
errors: 0  warnings: 26  info: 0

warn  CM023 auth_digest_digestauth_success: depends on variables another request's script sets ({{echo_digest_nonce}}, {{echo_digest_realm}}); an agent calling this tool on its own won't have them.
warn  CM040 cookies_delete_cookies: GET whose name/path reads as a write ('Delete Cookies'); readOnlyHint set to false so clients ask before calling it.
warn  CM040 cookies_set_cookies: GET whose name/path reads as a write ('Set Cookies'); readOnlyHint set to false so clients ask before calling it.
warn  CM060 auth_digest_digestauth_success: auth type 'digest' is not replayed by the generated server; calls will go out unauthenticated.
warn  CM060 auth_others_oauth1_0_verify_signature: auth type 'oauth1' is not replayed by the generated server; calls will go out unauthenticated.
... (21 more in demo/echo-lint.txt)

$ cmcp check examples/postman-echo-v2.postman_collection.json --call-readonly
protocol: 2025-06-18  tools listed: 22  schema problems: 0
read-only calls:
  FAIL auth_digest_digestauth_request: HTTP 401 (328 ms) GET https://postman-echo.com/digest-auth
  FAIL auth_digest_digestauth_success: Variable '{{echo_digest_realm}}' is not set. Pass --env <postman_environment.json> or export CMCP_VAR_echo_digest_realm=<value>.
  ok   auth_others_basic_auth: HTTP 200 (271 ms) GET https://postman-echo.com/basic-auth
  FAIL auth_others_oauth1_0_verify_signature: HTTP 401 (274 ms) GET https://postman-echo.com/oauth1
  ok   cookies_get_cookies: HTTP 200 (291 ms) GET https://postman-echo.com/cookies
  ok   headers_request_headers: HTTP 200 (261 ms) GET https://postman-echo.com/headers
  ok   headers_response_headers: HTTP 200 (268 ms) GET https://postman-echo.com/response-headers?Content-Type=text%2Fhtml&Server=apibin
  ok   request_methods_get_request: HTTP 200 (244 ms) GET https://postman-echo.com/get?test=123
  ok   utilities_response_status_code: HTTP 200 (255 ms) GET https://postman-echo.com/status/200
  ok   utilities_streamed_response: HTTP 200 (296 ms) GET https://postman-echo.com/stream/10
  ok   utilities_delay_response: HTTP 200 (3275 ms) GET https://postman-echo.com/delay/3
  ok   utilities_get_utf8_encoded_response: HTTP 200 (280 ms) GET https://postman-echo.com/encoding/utf8
  ok   utilities_gzip_compressed_response: HTTP 200 (263 ms) GET https://postman-echo.com/gzip
  ok   utilities_deflate_compressed_response: HTTP 200 (264 ms) GET https://postman-echo.com/deflate
  ok   draft_auth_oauth2_0_oauth2_0_get_resource: HTTP 200 (275 ms) GET https://postman-echo.com/oauth2/user/info
result: FAIL

Write tools (POST/PUT/PATCH/DELETE, and the two cookie GETs) are never called by check.

Tests

$ uv run --with pytest --with mcp python -m pytest -q
................                                                         [100%]
16 passed in 3.68s

The suite runs offline against a local HTTP mock: parsing (v2.0 and v2.1, auth inheritance, string URLs), naming, schemas, every lint rule on examples/orders-api.postman_collection.json (a fixture built to trip them), the JSON-RPC loop, check spawning the server as a subprocess, and one interop test that drives the server with the official MCP Python SDK client (mcp 2.3.0; skipped when it isn't installed).

Limits

  • Pre-request and test scripts are read only to find which variables they set. They aren't executed.

  • No digest, OAuth 1.0, Hawk, AWS SigV4 or NTLM signing (lint warns instead).

  • formdata file fields are sent as plain text fields.

  • Mutating-GET detection is a word list (delete, set, cancel, ...). It will miss some.

Files

cmcp/collection.py  parse v2.0/v2.1 collections and environments
cmcp/tools.py       requests -> MCP tool definitions
cmcp/lint.py        agent-readiness rules
cmcp/runtime.py     variable resolution + HTTP call
cmcp/server.py      MCP stdio server (JSON-RPC 2.0)
cmcp/check.py       stdio client that validates a running server
examples/           Postman Echo collection (from postmanlabs/newman, Apache-2.0) + a fixture

examples/postman-echo-v2.postman_collection.json is copied unchanged from postmanlabs/newman (Apache-2.0). Everything else is MIT.

Available Tools

22 tools
auth_digest_digestauth_requestAuth: Digest/DigestAuth RequestA
Read-only

Performing a simple GET request to this endpoint returns status code 401 Unauthorized with WWW-Authenticate header containing information to successfully authenticate subsequent requests. The WWW-Authenticate header must be processed to extract realm and nonce values to hash subsequent requests.

When this request is executed within Postman, the script attached with this request does the hard work of extracting realm and nonce from the header and set it as global variables named echo_digest_nonce and echo_digest_realm. These variables are re-used in subsequent request for seamless integration of the two requests.

(Postman request: Auth: Digest/DigestAuth Request | GET https://postman-echo.com/digest-auth)

Inputs: no named parameters.

auth_digest_digestauth_successAuth: Digest/DigestAuth SuccessA
Read-only

This endpoint sends a hashed Digest Authorization header to gain access to a valid 200 Ok response code. In Postman, it uses the stored global variables, echo_digest_realm and echo_digest_nonce, to generate the hashed authorisation header.

Within Postman, for this request to successfully authenticate, running the previous request "DigestAuth Request" stores the relevant information within the global variables.

(Postman request: Auth: Digest/DigestAuth Success | GET https://postman-echo.com/digest-auth)

Inputs: no named parameters.

auth_others_basic_authAuth: Others/Basic AuthA
Read-only

This endpoint simulates a basic-auth protected endpoint. The endpoint accepts a default username and password and returns a status code of 200 ok only if the same is provided. Otherwise it will return a status code 401 unauthorized.

Username: postman

Password: password

To use this endpoint, send a request with the header Authorization: Basic cG9zdG1hbjpwYXNzd29yZA==. The cryptic latter half of the header value is a base64 encoded concatenation of the default username and password. Using Postman, to send this request, you can simply fill in the username and password in the "Authorization" tab and Postman will do the rest for you.

To know more about basic authentication, refer to the Basic Access Authentication wikipedia article. The article on authentication helpers elaborates how to use the same within the Postman app.

(Postman request: Auth: Others/Basic Auth | GET https://postman-echo.com/basic-auth)

Inputs: no named parameters.

auth_others_oauth1_0_verify_signatureAuth: Others/OAuth1.0 Verify SignatureA
Read-only

OAuth1.0a is a specification that defines a protocol that can be used by one service to access "protected" resources (endpoints) on another service. A major part of OAuth1.0 is HTTP Request Signing. This endpoint allows you to check whether the request calculation works properly in the client.

The endpoint supports the HTTP Authorization header. In case the signature verification fails, the endpoint provides the four debug values,

  • base_uri

  • normalized_param_string

  • base_string

  • signing_key

For more details about these parameters, check the OAuth1.0a Specification

In order to use this endpoint, you can set the following values:

Consumer Key: RKCGzna7bv9YD57c

Consumer Secret: D+EdQ-gs$-%@2Nu7

If you are using Postman, also check the "Add params to header" and "Auto add parameters" boxes.

(Postman request: Auth: Others/OAuth1.0 Verify Signature | GET https://postman-echo.com/oauth1)

Inputs: no named parameters.

cookies_delete_cookiesCookies/Delete CookiesB
Destructive

One or more cookies that has been set for this domain can be deleted by providing the cookie names as part of the URL parameter. The response of this request is a JSON containing the list of currently set cookies.

(Postman request: Cookies/Delete Cookies | GET https://postman-echo.com/cookies/delete)

Inputs: foo1, foo2.

cookies_get_cookiesCookies/Get CookiesA
Read-only

Use this endpoint to get a list of all cookies that are stored with respect to this domain. Whatever key-value pairs that has been previously set by calling the "Set Cookies" endpoint, will be returned as response JSON.

(Postman request: Cookies/Get Cookies | GET https://postman-echo.com/cookies)

Inputs: no named parameters.

cookies_set_cookiesCookies/Set CookiesA

The cookie setter endpoint accepts a list of cookies and their values as part of URL parameters of a GET request. These cookies are saved and can be subsequently retrieved or deleted. The response of this request returns a JSON with all cookies listed.

To set your own set of cookies, simply replace the URL parameters "foo1=bar1&foo2=bar2" with your own set of key-value pairs.

(Postman request: Cookies/Set Cookies | GET https://postman-echo.com/cookies/set)

Inputs: foo1, foo2.

draft_auth_oauth2_0_oauth2_0_get_access_token[draft] Auth: OAuth2.0/OAuth2.0 Get Access TokenA

This endpoint is used to get the access_token

It requires the following secret client credentials to be sent as part of the form body along with the authentication code obtained as part of the redirect_uri from the previous request.

code: xWnkliVQJURqB2x1

grant_type: authorization_code

redirect_uri: https://www.getpostman.com/oauth2/callback

client_id: abc123

client_secret: ssh-secret

If the correct credentials are not passed, the server returns with a 401 Unauthorized response.

(Postman request: [draft] Auth: OAuth2.0/OAuth2.0 Get Access Token | POST https://postman-echo.com/oauth2/token)

Inputs: form.

draft_auth_oauth2_0_oauth2_0_get_resource[draft] Auth: OAuth2.0/OAuth2.0 Get ResourceC
Read-only

Once the bearer token has been obtained, it can be passed as Authorization header to access user's resources.

Authorization: Bearer vp7jx...

If the Authorization header is not passed the endpoint returns 401 Unauthorized.

(Postman request: [draft] Auth: OAuth2.0/OAuth2.0 Get Resource | GET https://postman-echo.com/oauth2/user/info)

Inputs: no named parameters.

headers_request_headersHeaders/Request HeadersB
Read-only

A GET request to this endpoint returns the list of all request headers as part of the response JSON. In Postman, sending your own set of headers through the Headers tab will reveal the headers as part of the response.

(Postman request: Headers/Request Headers | GET https://postman-echo.com/headers)

Inputs: no named parameters.

headers_response_headersHeaders/Response HeadersA
Read-only

This endpoint causes the server to send custom set of response headers. Providing header values as part of the URL parameters of a GET request to this endpoint returns the same as part of response header.

To send your own set of headers, simply add or replace the the URL parameters with your own set.

(Postman request: Headers/Response Headers | GET https://postman-echo.com/response-headers)

Inputs: Server, Content-Type.

request_methods_delete_requestRequest Methods/DELETE RequestB
Destructive

The HTTP DELETE method is used to delete resources on a server. The exact use of DELETE requests depends on the server implementation. In general, DELETE requests support both, Query String parameters as well as a Request Body.

This endpoint accepts an HTTP DELETE request and provides debug information such as the HTTP headers, Query String arguments, and the Request Body.

(Postman request: Request Methods/DELETE Request | DELETE https://postman-echo.com/delete)

Inputs: body.

request_methods_get_requestRequest Methods/GET RequestB
Read-only

The HTTP GET request method is meant to retrieve data from a server. The data is identified by a unique URI (Uniform Resource Identifier).

A GET request can pass parameters to the server using "Query String Parameters". For example, in the following request,

http://example.com/hi/there?hand=wave

The parameter "hand" has the value "wave".

This endpoint echoes the HTTP headers, request parameters and the complete URI requested.

(Postman request: Request Methods/GET Request | GET https://postman-echo.com/get)

Inputs: test.

request_methods_patch_requestRequest Methods/PATCH RequestC
Destructive

The HTTP PATCH method is used to update resources on a server. The exact use of PATCH requests depends on the server in question. There are a number of server implementations which handle PATCH differently. Technically, PATCH supports both Query String parameters and a Request Body.

This endpoint accepts an HTTP PATCH request and provides debug information such as the HTTP headers, Query String arguments, and the Request Body.

(Postman request: Request Methods/PATCH Request | PATCH https://postman-echo.com/patch)

Inputs: body.

request_methods_post_requestRequest Methods/POST RequestB

The HTTP POST request method is meant to transfer data to a server (and elicit a response). What data is returned depends on the implementation of the server.

A POST request can pass parameters to the server using "Query String Parameters", as well as the Request Body. For example, in the following request,

POST /hi/there?hand=wave

The parameter "hand" has the value "wave". The request body can be in multiple formats. These formats are defined by the MIME type of the request. The MIME Type can be set using the Content-Type HTTP header. The most commonly used MIME types are:

  • multipart/form-data

  • application/x-www-form-urlencoded

  • application/json

This endpoint echoes the HTTP headers, request parameters, the contents of the request body and the complete URI requested.

(Postman request: Request Methods/POST Request | POST https://postman-echo.com/post)

Inputs: body.

request_methods_put_requestRequest Methods/PUT RequestC
Destructive

The HTTP PUT request method is similar to HTTP POST. It too is meant to transfer data to a server (and elicit a response). What data is returned depends on the implementation of the server.

A PUT request can pass parameters to the server using "Query String Parameters", as well as the Request Body. For example, in the following raw HTTP request,

PUT /hi/there?hand=wave

(Postman request: Request Methods/PUT Request | PUT https://postman-echo.com/put)

Inputs: body.

utilities_deflate_compressed_responseUtilities/Deflate Compressed ResponseA
Read-only

This endpoint returns the response using deflate compression algorithm. The uncompressed response is a JSON string containing the details of the request sent by the client. For this endpoint to work, one should request with Accept-encoding header containing deflate as part of its value. Postman supports gzip, deflate and SDCH decoding and automatically sends them as part of the request.

HTTP Compression allows the server to send responses in a compressed format, which is uncompressed by the client before processing. This reduces network bandwidth consumption at the cost of increase in CPU usage. To know more about this, refer the HTTP Compression wikipedia article.

(Postman request: Utilities/Deflate Compressed Response | GET https://postman-echo.com/deflate)

Inputs: no named parameters.

utilities_delay_responseUtilities/Delay ResponseB
Read-only

Using this endpoint one can configure how long it takes for the server to come back with a response. Appending a number to the URL defines the time (in seconds) the server will wait before responding.

Note that a maximum delay of 10 seconds is accepted by the server.

(Postman request: Utilities/Delay Response | GET https://postman-echo.com/delay/3)

Inputs: no named parameters.

utilities_get_utf8_encoded_responseUtilities/Get UTF8 Encoded ResponseA
Read-only

If a response of an endpoint requires to send data beyond the basic English / ASCII character set, the charset parameter in the Content-Type response header defines the character encoding policy.

This endpoint returns an UTF8 character encoded response body with text in various languages such as Greek, Latin, East Asian, etc. Postman can interpret the character encoding and use appropriate methods to display the character set in responses.

(Postman request: Utilities/Get UTF8 Encoded Response | GET https://postman-echo.com/encoding/utf8)

Inputs: no named parameters.

utilities_gzip_compressed_responseUtilities/GZip Compressed ResponseA
Read-only

This endpoint returns the response using gzip compression algorithm. The uncompressed response is a JSON string containing the details of the request sent by the client. For this endpoint to work, one should request with Accept-encoding header containing gzip as part of its value. Postman supports gzip, deflate and SDCH decoding and automatically sends them as part of the request.

HTTP Compression allows the server to send responses in a compressed format, which is uncompressed by the client before processing. This reduces network bandwidth consumption at the cost of increase in CPU usage. To know more about this, refer the HTTP Compression wikipedia article.

(Postman request: Utilities/GZip Compressed Response | GET https://postman-echo.com/gzip)

Inputs: no named parameters.

utilities_response_status_codeUtilities/Response Status CodeC
Read-only

This endpoint allows one to instruct the server which status code to respond with.

Every response is accompanied by a status code. The status code provides a summary of the nature of response sent by the server. For example, a status code of 200 means everything is okay with the response and a code of 404 implies that the requested URL does not exist on server. A list of all valid HTTP status code can be found at the List of Status Codes wikipedia article. When using Postman, the response status code is described for easy reference.

Note that if an invalid status code is requested to be sent, the server returns a status code of 400 Bad Request.

(Postman request: Utilities/Response Status Code | GET https://postman-echo.com/status/200)

Inputs: no named parameters.

utilities_streamed_responseUtilities/Streamed ResponseA
Read-only

This endpoint allows one to receive streaming http response using chunked transfer encoding of a configurable length.

A streaming response does not wait for the entire response to be generated on server before flushing it out. This implies that for a fairly large response, parts of it can be streamed to the requestee as and when it is generated on server. The client can then take actions of processing this partially received data.

(Postman request: Utilities/Streamed Response | GET https://postman-echo.com/stream/10)

Inputs: no named parameters.

Tool Schema Changelog

Recent tool additions, removals, and schema changes observed during successful MCP inspections.

Loading tool schema changelog…

    TDQS

    B3.1/5.0

    Scored across 22 tools

    Disambiguation4/5

    Most tools map to a single, distinct postman-echo endpoint, so an agent can generally pick the right one (cookies get/set/delete, each HTTP method, each auth flavor). The main soft spot is the pair headers_request_headers vs headers_response_headers, whose descriptions are easy to invert, and the two-step digest pair requires understanding that digestauth_request must precede digestauth_success. Otherwise boundaries are clear.

    Naming Consistency3/5

    Names follow a rough category_prefix_action scheme, but the verb is inconsistently present (cookies_get_cookies vs auth_others_basic_auth vs headers_request_headers) and the oauth2 draft tools suffer from stuttering redundancy (draft_auth_oauth2_0_oauth2_0_get_access_token). It is readable and prefix-grouped, but not a predictable verb_noun pattern throughout.

    Tool Count3/5

    22 tools is on the heavy side for what is essentially a mirror of the postman-echo demo endpoint collection. Several tools are near-duplicates in spirit (gzip vs deflate, the digest two-step, two separate OAuth1/OAuth2 groups), which inflates the count beyond what a typical agent workflow needs.

    Completeness4/5

    The surface covers HTTP methods (GET/POST/PUT/PATCH/DELETE), cookie lifecycle, header echo/injection, and basic/digest/OAuth1/OAuth2 auth plus compression, streaming, delay and encoding utilities — a solid lifecycle for an echo service. Gaps are minor: no HEAD/OPTIONS, no /ip, /uuid, /json, /xml, or /image endpoints, and the digest flow depends on Postman global-variable state an MCP agent cannot easily carry across calls.

    Maintenance

    ActivityMaintained
    ResponsivenessNo recent community issues

    Related MCP Connectors

    Related MCP Servers

    • A
      license
      B
      quality
      D
      maintenance
      An MCP server that generates AI agent tools from Postman collections and requests. This server integrates with the Postman API to convert API endpoints into type-safe code that can be used with various AI frameworks.
      1
      13
      MIT
    • F
      license
      Not graded
      quality
      Not graded
      maintenance
      Generates MCP servers from Postman API collections, automatically converting selected API requests into MCP-compatible tools that can be used with Claude Desktop and other MCP clients.
      -
    • A
      license
      Not graded
      quality
      D
      maintenance
      Generates MCP servers from Postman API requests, automatically creating JavaScript tools that enable LLMs to interact with any API documented in Postman collections. Supports authentication configuration and deployment via STDIO or SSE.
      MIT
    • A
      license
      Not graded
      quality
      D
      maintenance
      Serves any OpenAPI 3.x/Swagger 2.x API as a local MCP server over stdio, converting every operation into a tool that proxies requests to the upstream API with configurable headers and fixed parameters.
      10 npm
      1
      MIT